1 #include "e.h"
2 
3 /* yes - i know. glibc specific... but i like being able to do my own */
4 /* backtraces! NB: you need CFLAGS="-rdynamic -g" LDFLAGS="-rdynamic -g" */
5 #ifdef OBJECT_PARANOIA_CHECK
6 
7 /* local subsystem functions */
8 static void _e_object_segv(int sig);
9 
10 /* local subsystem globals */
11 static sigjmp_buf _e_object_segv_buf;
12 #endif
13 
14 /* externally accessible functions */
15 E_API void *
e_object_alloc(int size,int type,E_Object_Cleanup_Func cleanup_func)16 e_object_alloc(int size, int type, E_Object_Cleanup_Func cleanup_func)
17 {
18    E_Object *obj;
19 
20    obj = calloc(1, size);
21    if (!obj) return NULL;
22    obj->magic = E_OBJECT_MAGIC;
23    obj->type = type;
24    obj->references = 1;
25    obj->cleanup_func = cleanup_func;
26    return obj;
27 }
28 
29 static void
_delay_del(void * data)30 _delay_del(void *data)
31 {
32    E_Object *obj = data;
33 
34    obj->delay_del_job = NULL;
35    if (obj->del_att_func) obj->del_att_func(obj);
36    if (obj->del_func) obj->del_func(obj);
37    e_object_unref(obj);
38 }
39 
40 E_API void
e_object_del(E_Object * obj)41 e_object_del(E_Object *obj)
42 {
43    E_OBJECT_CHECK(obj);
44    if (obj->deleted) return;
45    if (obj->del_delay_func)
46      {
47         if (obj->ref_debug)
48           INF("[%p] DELAY DEL (REF: %d)", obj, obj->references);
49         obj->del_delay_func(obj);
50         if (!obj->delay_del_job)
51           obj->delay_del_job = ecore_job_add(_delay_del, obj);
52         obj->deleted = 1;
53         return;
54      }
55    if (obj->ref_debug)
56      INF("[%p] DEL (REF: %d)", obj, obj->references);
57    obj->deleted = 1;
58    if (obj->del_att_func) obj->del_att_func(obj);
59    if (obj->del_func) obj->del_func(obj);
60    e_object_unref(obj);
61 }
62 
63 E_API void
e_object_delay_del_set(E_Object * obj,void * func)64 e_object_delay_del_set(E_Object *obj, void *func)
65 {
66    E_OBJECT_CHECK(obj);
67    obj->del_delay_func = func;
68 }
69 
70 E_API int
e_object_is_del(E_Object * obj)71 e_object_is_del(E_Object *obj)
72 {
73    E_OBJECT_CHECK_RETURN(obj, 1);
74    return obj->deleted;
75 }
76 
77 E_API void
e_object_del_func_set(E_Object * obj,E_Object_Cleanup_Func del_func)78 e_object_del_func_set(E_Object *obj, E_Object_Cleanup_Func del_func)
79 {
80    E_OBJECT_CHECK(obj);
81    obj->del_func = del_func;
82 }
83 
84 E_API void
e_object_type_set(E_Object * obj,int type)85 e_object_type_set(E_Object *obj, int type)
86 {
87    E_OBJECT_CHECK(obj);
88    obj->type = type;
89 }
90 
91 E_API void
e_object_free(E_Object * obj)92 e_object_free(E_Object *obj)
93 {
94    E_OBJECT_CHECK(obj);
95    if (obj->free_att_func) obj->free_att_func(obj);
96    obj->free_att_func = NULL;
97    obj->walking_list++;
98    while (obj->del_fn_list)
99      {
100         E_Object_Delfn *dfn = (E_Object_Delfn *)obj->del_fn_list;
101         if (!dfn->delete_me) dfn->func(dfn->data, obj);
102         obj->del_fn_list = eina_inlist_remove(obj->del_fn_list,
103                                               EINA_INLIST_GET(dfn));
104         free(dfn);
105      }
106    obj->walking_list--;
107    if (obj->references) return;
108    /*
109     * FIXME:
110     * although this is good - if during cleanup the cleanup func calls
111     * other generic funcs to do cleanups on the same object... we get bitching.
112     * disable for now (the final free of the struct should probably happen after
113     * the cleanup func and be done by the object system - set the magic after
114     * cleanup :)
115     */
116 #if 0
117    obj->magic = E_OBJECT_MAGIC_FREED;
118 #endif
119    obj->cleanup_func(obj);
120    obj = NULL;
121 }
122 
123 E_API int
e_object_ref(E_Object * obj)124 e_object_ref(E_Object *obj)
125 {
126    E_OBJECT_CHECK_RETURN(obj, -1);
127    obj->references++;
128    if (obj->ref_debug)
129      INF("[%p] REF (REF: %d)", obj, obj->references);
130    return obj->references;
131 }
132 
133 E_API int
e_object_unref(E_Object * obj)134 e_object_unref(E_Object *obj)
135 {
136    int ref;
137 
138    E_OBJECT_CHECK_RETURN(obj, -1);
139    if (!obj->references) return 0;
140    obj->references--;
141    ref = obj->references;
142    if (obj->ref_debug)
143      {
144         if ((ref > 1) && (ref < 1000))
145           INF("[%p] UNREF (REF: %d)", obj, ref);
146         else if (ref == 1)
147           INF("[%p] UNREF (REF: %d)", obj, ref);
148         else if (!ref)
149           INF("[%p] UNREF (REF: %d)", obj, ref);
150         else
151           CRI("[%p] UNREF (REF: %d)", obj, ref);
152      }
153    if (obj->references == 0) e_object_free(obj);
154    return ref;
155 }
156 
157 E_API int
e_object_ref_get(E_Object * obj)158 e_object_ref_get(E_Object *obj)
159 {
160    E_OBJECT_CHECK_RETURN(obj, 0);
161    return obj->references;
162 }
163 
164 #if 0
165 E_API void
166 e_bt(void)
167 {
168    int i, trace_num;
169    void *trace[128];
170    char **messages = NULL;
171    trace_num = backtrace(trace, 128);
172    messages = backtrace_symbols(trace, trace_num);
173    if (messages)
174      {
175         for (i = 1; i < trace_num; i++)
176           {
177              int j;
178 
179              for (j = 1; j < i; j++)
180                putchar(' ');
181              printf("%s\n", messages[i]);
182           }
183         free(messages);
184      }
185 }
186 
187 #endif
188 
189 E_API int
e_object_error(E_Object * obj)190 e_object_error(E_Object *obj)
191 {
192 #ifdef OBJECT_PARANOIA_CHECK
193    char buf[4096];
194    char bt[8192];
195    void *trace[128];
196    char **messages = NULL;
197    int i, trace_num;
198 
199    /* fetch stacktrace */
200    trace_num = backtrace(trace, 128);
201    messages = backtrace_symbols(trace, trace_num);
202 
203    /* build stacktrace */
204    bt[0] = 0;
205    if (messages)
206      {
207         for (i = 1; i < trace_num; i++)
208           {
209              strcat(bt, messages[i]);
210              strcat(bt, "\n");
211           }
212         free(messages);
213      }
214    /* if NULL obj then dump stacktrace */
215    if (!obj)
216      {
217         snprintf(buf, sizeof(buf),
218                  "Object is NULL.\n"
219                  "%s",
220                  bt);
221      }
222    /* if obj pointer is non NULL, actually try an access and see if we segv */
223    else if (obj)
224      {
225         struct sigaction act, oact;
226         int magic = 0, segv = 0;
227 
228         /* setup segv handler */
229         act.sa_handler = _e_object_segv;
230         act.sa_flags = SA_RESETHAND;
231         sigemptyset(&act.sa_mask);
232         sigaction(SIGSEGV, &act, &oact);
233         /* set a longjump to be within this if statement. only called if we */
234         /* segfault */
235         if (sigsetjmp(_e_object_segv_buf, 1))
236           {
237              sigaction(SIGSEGV, &oact, NULL);
238              segv = 1;
239           }
240         else
241           {
242              /* try access magic value */
243              magic = obj->magic;
244              /* if pointer is bogus we'd segv and so jump to the if () above */
245              /* contents, and thus reset segv handler and set segv flag. */
246              /* if not we just continue moving along here and reset handler */
247              sigaction(SIGSEGV, &oact, NULL);
248           }
249         /* if we segfaulted above... */
250         if (segv)
251           snprintf(buf, sizeof(buf),
252                    "Object [%p] is an invalid/garbage pointer.\n"
253                    "Segfault successfully avoided.\n"
254                    "%s",
255                    obj,
256                    bt);
257         else
258           {
259              /* valid ram then... if we freed this object before */
260              if (magic == E_OBJECT_MAGIC_FREED)
261                snprintf(buf, sizeof(buf),
262                         "Object [%p] is already freed.\n"
263                         "%s",
264                         obj,
265                         bt);
266              /* garbage magic value - pointer to non object */
267              else if (magic != E_OBJECT_MAGIC)
268                snprintf(buf, sizeof(buf),
269                         "Object [%p] has garbage magic (%x).\n"
270                         "%s",
271                         obj, magic,
272                         bt);
273              else if (obj->references < 0)
274                snprintf(buf, sizeof(buf),
275                         "Object [%p] has negative references (%i).\n"
276                         "%s",
277                         obj, obj->references,
278                         bt);
279              else if (obj->references > 100)
280                snprintf(buf, sizeof(buf),
281                         "Object [%p] has unusually high reference count (%i).\n"
282                         "%s",
283                         obj, obj->references,
284                         bt);
285              /* it's all ok! */
286              else
287                {
288                   return 0;
289                }
290           }
291      }
292    /* display actual error message */
293    e_error_message_show("%s", buf);
294    return 1;
295 #else
296    if (!obj)
297      {
298         fprintf(stderr, "ERROR: Object is NULL. Add break point at %s:%d\n",
299                 __FILE__, __LINE__);
300         return 1;
301      }
302    return 0;
303 #endif
304 }
305 
306 E_API void
e_object_data_set(E_Object * obj,const void * data)307 e_object_data_set(E_Object *obj, const void *data)
308 {
309    E_OBJECT_CHECK(obj);
310    obj->data = (void *)data;
311 }
312 
313 E_API void *
e_object_data_get(E_Object * obj)314 e_object_data_get(E_Object *obj)
315 {
316    E_OBJECT_CHECK_RETURN(obj, NULL);
317    return obj->data;
318 }
319 
320 E_API void
e_object_free_attach_func_set(E_Object * obj,E_Object_Cleanup_Func func)321 e_object_free_attach_func_set(E_Object *obj, E_Object_Cleanup_Func func)
322 {
323    E_OBJECT_CHECK(obj);
324    obj->free_att_func = func;
325 }
326 
327 E_API void
e_object_del_attach_func_set(E_Object * obj,E_Object_Cleanup_Func func)328 e_object_del_attach_func_set(E_Object *obj, E_Object_Cleanup_Func func)
329 {
330    E_OBJECT_CHECK(obj);
331    obj->del_att_func = func;
332 }
333 
334 E_API void
e_object_ref_debug_set(E_Object * obj,Eina_Bool set)335 e_object_ref_debug_set(E_Object *obj, Eina_Bool set)
336 {
337    E_OBJECT_CHECK(obj);
338    obj->ref_debug = !!set;
339 }
340 
341 E_API void
e_object_delfn_clear(E_Object * obj)342 e_object_delfn_clear(E_Object *obj)
343 {
344    E_Object_Delfn *dfn;
345 
346    E_OBJECT_CHECK(obj);
347    if (obj->walking_list)
348      {
349         EINA_INLIST_FOREACH(obj->del_fn_list, dfn)
350           {
351              dfn->delete_me = 1;
352           }
353         return;
354      }
355    while (obj->del_fn_list)
356      {
357         dfn = (E_Object_Delfn *)obj->del_fn_list;
358         obj->del_fn_list = eina_inlist_remove(obj->del_fn_list,
359                                               EINA_INLIST_GET(dfn));
360         free(dfn);
361      }
362 }
363 
364 E_API E_Object_Delfn *
e_object_delfn_add(E_Object * obj,void (* func)(void * data,void * obj),void * data)365 e_object_delfn_add(E_Object *obj, void (*func)(void *data, void *obj), void *data)
366 {
367    E_Object_Delfn *dfn;
368    E_OBJECT_CHECK_RETURN(obj, NULL);
369    dfn = calloc(1, sizeof(E_Object_Delfn));
370    if (!dfn) return NULL;
371    dfn->func = func;
372    dfn->data = data;
373    obj->del_fn_list = eina_inlist_append(obj->del_fn_list, EINA_INLIST_GET(dfn));
374    return dfn;
375 }
376 
377 E_API void
e_object_delfn_del(E_Object * obj,E_Object_Delfn * dfn)378 e_object_delfn_del(E_Object *obj, E_Object_Delfn *dfn)
379 {
380    E_OBJECT_CHECK(obj);
381    if (obj->walking_list)
382      {
383         dfn->delete_me = 1;
384         return;
385      }
386    obj->del_fn_list = eina_inlist_remove(obj->del_fn_list, EINA_INLIST_GET(dfn));
387    free(dfn);
388 }
389 
390 /*
391    void
392    e_object_breadcrumb_add(E_Object *obj, char *crumb)
393    {
394    E_OBJECT_CHECK(obj);
395    obj->crumbs = eina_list_append(obj->crumbs, strdup(crumb));
396    }
397 
398    void
399    e_object_breadcrumb_del(E_Object *obj, char *crumb)
400    {
401    Eina_List *l;
402    char *key;
403 
404    E_OBJECT_CHECK(obj);
405    EINA_LIST_FOREACH(obj->crumbs, l, key)
406      {
407         if (!strcmp(crumb, key))
408           {
409              free(key);
410              obj->crumbs = eina_list_remove_list(obj->crumbs, l);
411              return;
412           }
413      }
414    }
415 
416    void
417    e_object_breadcrumb_debug(E_Object *obj)
418    {
419    Eina_List *l;
420    char *key;
421 
422    E_OBJECT_CHECK(obj);
423    EINA_LISt_FOREACH(obj->crumbs, l, key)
424      printf("CRUMB: %s\n", key);
425    }
426  */
427 
428 #ifdef OBJECT_PARANOIA_CHECK
429 /* local subsystem functions */
430 static void
_e_object_segv(int sig)431 _e_object_segv(int sig)
432 {
433    siglongjmp(_e_object_segv_buf, 1);
434 }
435 
436 #endif
437