01db8a289aea7fcf35ddee06fbd39d907f6681fc
[platform/upstream/efl.git] / src / lib / eo / eo_ptr_indirection.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include "eo_ptr_indirection.h"
6
7 extern Eina_Thread _efl_object_main_thread;
8
9 //////////////////////////////////////////////////////////////////////////
10
11 Eina_TLS          _eo_table_data;
12 Eo_Id_Data       *_eo_table_data_shared = NULL;
13 Eo_Id_Table_Data *_eo_table_data_shared_data = NULL;
14
15 //////////////////////////////////////////////////////////////////////////
16
17 void
18 _eo_pointer_error(const Eo *obj_id, const char *func_name, const char *file, int line, const char *fmt, ...)
19 {
20    /* NOTE: this function exists to allow easy breakpoint on pointer errors */
21    va_list args;
22    va_start(args, fmt);
23    eina_log_vprint(_eo_log_dom, EINA_LOG_LEVEL_ERR, file, func_name, line, fmt, args);
24    va_end(args);
25    _eo_log_obj_report((Eo_Id)obj_id, EINA_LOG_LEVEL_ERR, func_name, file, line);
26 }
27
28 static void
29 _eo_obj_pointer_invalid(const Eo_Id obj_id,
30                         Eo_Id_Data *data,
31                         unsigned char domain,
32                         const char *func_name,
33                         const char *file,
34                         int line)
35 {
36    Eina_Thread thread = eina_thread_self();
37    const char *tself = "main";
38    const char *type = "object";
39    const char *reason = "This ID has probably been deleted";
40    char tbuf[128];
41    if (obj_id & MASK_CLASS_TAG) type = "class";
42    if (thread != _efl_object_main_thread)
43      {
44         snprintf(tbuf, sizeof(tbuf), "%p", (void *)thread);
45         tself = tbuf;
46      }
47
48    if (!data->tables[(int)data->local_domain])
49      reason = "This ID does not seem to belong to this thread";
50    else if ((Efl_Id_Domain)domain == EFL_ID_DOMAIN_SHARED)
51      reason = "This shared ID has probably been deleted";
52
53    eina_log_print(_eo_log_dom, EINA_LOG_LEVEL_ERR,
54                   file, func_name, line,
55                   "Eo ID %p is not a valid %s. "
56                   "Current thread: %s. "
57                   "%s or this was never a valid %s ID. "
58                   "(domain=%i, current_domain=%i, local_domain=%i, "
59                   "available_domains=[%s %s %s %s], "
60                   "generation=%lx, id=%lx, ref=%i)",
61                   (void *)obj_id,
62                   type,
63                   tself,
64                   reason,
65                   type,
66                   (int)domain,
67                   (int)data->domain_stack[data->stack_top],
68                   (int)data->local_domain,
69                   (data->tables[0]) ? "0" : " ",
70                   (data->tables[1]) ? "1" : " ",
71                   (data->tables[2]) ? "2" : " ",
72                   (data->tables[3]) ? "3" : " ",
73                   (unsigned long)(obj_id & MASK_GENERATIONS),
74                   (unsigned long)(obj_id >> SHIFT_ENTRY_ID) & (MAX_ENTRY_ID | MAX_TABLE_ID | MAX_MID_TABLE_ID),
75                   (int)(obj_id >> REF_TAG_SHIFT) & 0x1);
76    _eo_log_obj_report(obj_id, EINA_LOG_LEVEL_ERR, func_name, file, line);
77 }
78
79 _Eo_Object *
80 _eo_obj_pointer_get(const Eo_Id obj_id, const char *func_name, const char *file, int line)
81 {
82    _Eo_Id_Entry *entry;
83    Generation_Counter generation;
84    Table_Index mid_table_id, table_id, entry_id;
85    Eo_Id tag_bit;
86    Eo_Id_Data *data;
87    Eo_Id_Table_Data *tdata;
88    unsigned char domain;
89
90    // NULL objects will just be sensibly ignored. not worth complaining
91    // every single time.
92
93    data = _eo_table_data_get();
94    EINA_PREFETCH(&(data->tables[0]));
95    domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
96    tdata = _eo_table_data_table_get(data, domain);
97    EINA_PREFETCH(&(tdata->cache.id));
98    if (EINA_UNLIKELY(!tdata)) goto err;
99
100
101    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
102      {
103         if (obj_id == tdata->cache.id)
104           return tdata->cache.object;
105
106         mid_table_id = (obj_id >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;
107         EINA_PREFETCH(&(tdata->eo_ids_tables[mid_table_id]));
108         table_id = (obj_id >> SHIFT_TABLE_ID) & MASK_TABLE_ID;
109         entry_id = (obj_id >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;
110         generation = obj_id & MASK_GENERATIONS;
111
112         // get tag bit to check later down below - pipelining
113         tag_bit = (obj_id) & MASK_OBJ_TAG;
114         if (!obj_id) goto err_null;
115         else if (!tag_bit) goto err;
116
117         // Check the validity of the entry
118         if (tdata->eo_ids_tables[mid_table_id])
119           {
120              _Eo_Ids_Table *tab = TABLE_FROM_IDS;
121
122              if (tab)
123                {
124                   entry = &(tab->entries[entry_id]);
125                   if (entry->active && (entry->generation == generation))
126                     {
127                        // Cache the result of that lookup
128                        tdata->cache.object = entry->ptr;
129                        tdata->cache.id = obj_id;
130                        return entry->ptr;
131                     }
132                }
133           }
134         goto err;
135      }
136    else
137      {
138         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
139         if (obj_id == tdata->cache.id)
140         // yes we return keeping the lock locked. thats why
141         // you must call _eo_obj_pointer_done() wrapped
142         // by EO_OBJ_DONE() to release
143           return tdata->cache.object;
144
145         mid_table_id = (obj_id >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;
146         EINA_PREFETCH(&(tdata->eo_ids_tables[mid_table_id]));
147         table_id = (obj_id >> SHIFT_TABLE_ID) & MASK_TABLE_ID;
148         entry_id = (obj_id >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;
149         generation = obj_id & MASK_GENERATIONS;
150
151         // get tag bit to check later down below - pipelining
152         tag_bit = (obj_id) & MASK_OBJ_TAG;
153         if (!obj_id) goto err_shared_null;
154         else if (!tag_bit) goto err_shared;
155
156         // Check the validity of the entry
157         if (tdata->eo_ids_tables[mid_table_id])
158           {
159              _Eo_Ids_Table *tab = TABLE_FROM_IDS;
160
161              if (tab)
162                {
163                   entry = &(tab->entries[entry_id]);
164                   if (entry->active && (entry->generation == generation))
165                     {
166                        // Cache the result of that lookup
167                        tdata->cache.object = entry->ptr;
168                        tdata->cache.id = obj_id;
169                        // yes we return keeping the lock locked. thats why
170                        // you must call _eo_obj_pointer_done() wrapped
171                        // by EO_OBJ_DONE() to release
172                        return entry->ptr;
173                     }
174                }
175           }
176         goto err_shared;
177      }
178 err_shared_null:
179    eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
180 err_null:
181    eina_log_print(_eo_log_dom,
182                   EINA_LOG_LEVEL_DBG,
183                   file, func_name, line,
184                   "obj_id is NULL. Possibly unintended access?");
185    return NULL;
186 err_shared:
187    eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
188 err:
189    _eo_obj_pointer_invalid(obj_id, data, domain, func_name, file, line);
190    return NULL;
191 }