eo: Turn thread-validation off for unstable Tizen 5.0 apps which are violating thread...
[platform/upstream/efl.git] / src / lib / eo / eo_ptr_indirection.x
1 #include <assert.h>
2 #ifdef HAVE_MMAP
3 #include <sys/types.h>
4 #include <sys/stat.h>
5 #include <fcntl.h>
6 #include <sys/mman.h>
7 #endif
8
9 #ifdef HAVE_VALGRIND
10 # include <valgrind.h>
11 # include <memcheck.h>
12 #endif
13
14 /* Start of pointer indirection:
15  *
16  * This feature is responsible of hiding from the developer the real pointer of
17  * the Eo object to supply a better memory management by preventing bad usage
18  * of the pointers.
19  *
20  * Eo * is no more a pointer but indexes to an entry into an ids table.
21  * For a better memory usage:
22  * - a tree structure is used, composed of a top level table pointing at
23  *   mid tables pointing at tables composed of entries.
24  * - tables are allocated when needed (i.e no more empty entries in allocated tables.
25  * - empty tables are freed, except one kept as spare table.
26  *
27  * An Eo id is contructed by bits manipulation of table indexes and a generation.
28  *
29  * id = Mid Table | Table | Entry | Generation
30  *
31  * Generation helps finding abuse of ids. When an entry is assigned to an
32  * object, a generation is inserted into the id. If the developer uses this id
33  * although the object is freed and another one has replaced it into the same
34  * entry of the table, the generation will be different and an error will
35  * occur when accessing with the old id.
36  *
37  * Each Table is composed of:
38  * - an index 'start' indicating which free entry is the next one to use.
39  * - 2 indexes 'fifo_head' and 'fifo_tail' defining a fifo,
40  *   that will help us to store the entries to be reused. It stores only the
41  *   entries that have been used at least one time. The entries that have
42  *   never been used are "pointed" by the start parameter.
43  * - entries composed of:
44  *    - a pointer to the object
45  *    - an index 'next_in_fifo' used to chain the free entries in the fifo
46  *    - a flag indicating if the entry is active
47  *    - a generation assigned to the object
48  *
49  * When an entry is searched into a table, we first use one of the entries that
50  * has never been used. If there is none, we try to pop from the fifo.
51  * If a such entry doesn't exist, we pass to the next table.
52  * When an entry is found, we reserve it to the object pointer
53  * then contruct and return the related Eo id.
54  *
55  * Assigning all the entries of a table before trying to reuse them from
56  * the fifo ensures that we are not going to soon recycle a released entry,
57  * thus minimize the risks of an aggressive del() then use() on a single entry.
58  *
59  * The indexes and a reference to the last table which served an entry is kept
60  * and is reused prior to the others untill it is full.
61  * When an object is freed, the entry into the table is released by appending
62  * it to the fifo.
63  */
64
65 // enable this to test and use all 64bits of a pointer, otherwise limit to
66 // 47 bits because of luajit. it wants to check if any bits in the upper 17 are
67 // set for a sanity check for lightuserdata ... basically it does this:
68 // #define checklightudptr(L, p) (((uint64_t)(p) >> 47) ? (lj_err_msg(L, LJ_ERR_BADLU), NULL) : (p))
69 //#define EO_FULL64BIT 1
70
71 #if SIZEOF_UINTPTR_T == 4
72 /* 32 bits */
73 # define BITS_MID_TABLE_ID        5
74 # define BITS_TABLE_ID            5
75 # define BITS_ENTRY_ID           11
76 # define BITS_GENERATION_COUNTER  7
77 # define BITS_DOMAIN              2
78 # define BITS_CLASS               1
79 # define REF_TAG_SHIFT           31
80 # define DROPPED_TABLES           0
81 # define DROPPED_ENTRIES          4
82 typedef int16_t Table_Index;
83 typedef uint16_t Generation_Counter;
84 #else
85 # ifndef EO_FULL64BIT
86 /* 47 bits */
87 #  define BITS_MID_TABLE_ID       11
88 #  define BITS_TABLE_ID           11
89 #  define BITS_ENTRY_ID           11
90 #  define BITS_GENERATION_COUNTER 10
91 #  define BITS_DOMAIN              2
92 #  define BITS_CLASS               1
93 #  define REF_TAG_SHIFT           46
94 #  define DROPPED_TABLES           2
95 #  define DROPPED_ENTRIES          3
96 typedef int16_t Table_Index;
97 typedef uint16_t Generation_Counter;
98 # else
99 /* 64 bits */
100 #  define BITS_MID_TABLE_ID       11
101 #  define BITS_TABLE_ID           11
102 #  define BITS_ENTRY_ID           11
103 #  define BITS_GENERATION_COUNTER 27
104 #  define BITS_DOMAIN              2
105 #  define BITS_CLASS               1
106 #  define REF_TAG_SHIFT           63
107 #  define DROPPED_TABLES           2
108 #  define DROPPED_ENTRIES          3
109 typedef int16_t Table_Index;
110 typedef uint32_t Generation_Counter;
111 # endif
112 #endif
113
114 /* Shifts macros to manipulate the Eo id */
115 #define SHIFT_DOMAIN          (BITS_MID_TABLE_ID + BITS_TABLE_ID + \
116                                BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
117 #define SHIFT_MID_TABLE_ID    (BITS_TABLE_ID + \
118                                BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
119 #define SHIFT_TABLE_ID        (BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
120 #define SHIFT_ENTRY_ID        (BITS_GENERATION_COUNTER)
121
122 /* Maximum ranges - a few tables and entries are dropped to minimize the amount
123  * of wasted bytes, see _eo_id_mem_alloc */
124 #define MAX_DOMAIN            (1 << BITS_DOMAIN)
125 #define MAX_MID_TABLE_ID      (1 << BITS_MID_TABLE_ID)
126 #define MAX_TABLE_ID          ((1 << BITS_TABLE_ID) - DROPPED_TABLES )
127 #define MAX_ENTRY_ID          ((1 << BITS_ENTRY_ID) - DROPPED_ENTRIES)
128 #define MAX_GENERATIONS       (1 << BITS_GENERATION_COUNTER)
129
130 /* Masks */
131 #define MASK_DOMAIN           (MAX_DOMAIN - 1)
132 #define MASK_MID_TABLE_ID     (MAX_MID_TABLE_ID - 1)
133 #define MASK_TABLE_ID         ((1 << BITS_TABLE_ID) - 1)
134 #define MASK_ENTRY_ID         ((1 << BITS_ENTRY_ID) - 1)
135 #define MASK_GENERATIONS      (MAX_GENERATIONS - 1)
136 #define MASK_OBJ_TAG          (((Eo_Id) 1) << (REF_TAG_SHIFT))
137
138 /* This only applies to classes. Used to artificially enlarge the class ids
139  * to reduce the likelihood of a clash with normal integers. */
140 #define CLASS_TAG_SHIFT       (REF_TAG_SHIFT - 1)
141 #define MASK_CLASS_TAG        (((Eo_Id) 1) << (CLASS_TAG_SHIFT))
142
143 #define MEM_HEADER_SIZE       16
144 #define MEM_PAGE_SIZE         4096
145 #define MEM_MAGIC             0x3f61ec8a
146
147 typedef struct _Mem_Header
148 {
149    size_t size;
150    size_t magic;
151 } Mem_Header;
152
153 static void *
154 _eo_id_mem_alloc(size_t size)
155 {
156 #ifdef HAVE_MMAP
157 # ifdef HAVE_VALGRIND
158    if (RUNNING_ON_VALGRIND) return malloc(size);
159    else
160 # endif
161      {
162         void *ptr;
163         Mem_Header *hdr;
164         size_t newsize;
165         newsize = MEM_PAGE_SIZE * ((size + MEM_HEADER_SIZE + MEM_PAGE_SIZE - 1) / 
166                                    MEM_PAGE_SIZE);
167         ptr = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
168                    MAP_PRIVATE | MAP_ANON, -1, 0);
169         if (ptr == MAP_FAILED)
170           {
171              ERR("mmap of eo id table region failed!");
172              return NULL;
173           }
174         hdr = ptr;
175         hdr->size = newsize;
176         hdr->magic = MEM_MAGIC;
177         /* DBG("asked:%lu allocated:%lu wasted:%lu bytes", size, newsize, (newsize - size)); */
178         return (void *)(((unsigned char *)ptr) + MEM_HEADER_SIZE);
179      }
180 #else
181    return malloc(size);
182 #endif
183 }
184
185 static void *
186 _eo_id_mem_calloc(size_t num, size_t size)
187 {
188    void *ptr = _eo_id_mem_alloc(num * size);
189    if (!ptr) return NULL;
190    memset(ptr, 0, num * size);
191    return ptr;
192 }
193
194 static void
195 _eo_id_mem_free(void *ptr)
196 {
197 #ifdef HAVE_MMAP
198 # ifdef HAVE_VALGRIND
199    if (RUNNING_ON_VALGRIND) free(ptr);
200    else
201 # endif
202      {
203         Mem_Header *hdr;
204         if (!ptr) return;
205         hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
206         if (hdr->magic != MEM_MAGIC)
207           {
208              ERR("unmap of eo table region has bad magic!");
209              return;
210           }
211         munmap(hdr, hdr->size);
212      }
213 #else
214    free(ptr);
215 #endif
216 }
217
218 #ifdef EINA_DEBUG_MALLOC
219 static void
220 _eo_id_mem_protect(void *ptr, Eina_Bool may_not_write)
221 {
222 # ifdef HAVE_MMAP
223 #  ifdef HAVE_VALGRIND
224    if (RUNNING_ON_VALGRIND) { return; }
225    else
226 #  endif
227      {
228         Mem_Header *hdr;
229         if (!ptr) return;
230         hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
231         if (hdr->magic != MEM_MAGIC)
232           {
233              ERR("mprotect of eo table region has bad magic!");
234              return;
235           }
236         mprotect(hdr, hdr->size, PROT_READ | ( may_not_write ? 0 : PROT_WRITE) );
237      }
238 # endif
239 }
240 # define   PROTECT(_ptr_)   _eo_id_mem_protect((_ptr_), EINA_TRUE)
241 # define UNPROTECT(_ptr_)   _eo_id_mem_protect((_ptr_), EINA_FALSE)
242 #else
243 # define   PROTECT(_ptr_)
244 # define UNPROTECT(_ptr_)
245 #endif
246
247 #define EO_ALIGN_SIZE(size) eina_mempool_alignof(size)
248
249 /* Entry */
250 typedef struct
251 {
252    /* Pointer to the object */
253    _Eo_Object *ptr;
254    /* Indicates where to find the next entry to recycle */
255    Table_Index next_in_fifo;
256    /* Active flag */
257    unsigned int active     : 1;
258    /* Generation */
259    unsigned int generation : BITS_GENERATION_COUNTER;
260
261 } _Eo_Id_Entry;
262
263 /* Table */
264 typedef struct
265 {
266    /* Indicates where start the "never used" entries */
267    Table_Index start;
268    /* Indicates where to find the next entry to recycle */
269    Table_Index fifo_head;
270    /* Indicates where to add an entry to recycle */
271    Table_Index fifo_tail;
272    /* Packed mid table and table indexes */
273    Eo_Id partial_id;
274    /* Counter of free entries */
275    unsigned int free_entries;
276    /* Entries of the table holding real pointers and generations */
277    _Eo_Id_Entry entries[MAX_ENTRY_ID];
278 } _Eo_Ids_Table;
279
280 //////////////////////////////////////////////////////////////////////////
281
282 typedef struct _Eo_Id_Data       Eo_Id_Data;
283 typedef struct _Eo_Id_Table_Data Eo_Id_Table_Data;
284
285 struct _Eo_Id_Table_Data
286 {
287    /* Cached eoid lookups */
288    struct
289      {
290         Eo_Id             id;
291         _Eo_Object       *object;
292         const Eo         *isa_id;
293         const Efl_Class  *klass;
294         Eina_Bool         isa;
295      }
296    cache;
297    /* Tables handling pointers indirection */
298    _Eo_Ids_Table     **eo_ids_tables[MAX_MID_TABLE_ID];
299    /* Current table used for following allocations */
300    _Eo_Ids_Table      *current_table;
301    /* Spare empty table */
302    _Eo_Ids_Table      *empty_table;
303    /* Optional lock around all objects in eoid table - only used if shared */
304    Eina_Lock           obj_lock;
305    /* Next generation to use when assigning a new entry to a Eo pointer */
306    Generation_Counter  generation;
307    /* are we shared so we need lock/unlock? */
308    Eina_Bool           shared;
309 };
310
311 struct _Eo_Id_Data
312 {
313    Eo_Id_Table_Data   *tables[4];
314    unsigned char       local_domain;
315    unsigned char       stack_top;
316    unsigned char       domain_stack[255 - (sizeof(void *) * 4) - 2];
317 };
318
319 extern Eina_TLS          _eo_table_data;
320 extern Eo_Id_Data       *_eo_table_data_shared;
321 extern Eo_Id_Table_Data *_eo_table_data_shared_data;
322
323 /* TIZEN_ONLY(20180518): See _eo_table_data_get() */
324 #ifdef DISABLE_EO_THREAD_CHECK
325 extern Eo_Id_Data       *_tizen_eo_table_data;
326 #endif
327
328 static inline Eo_Id_Table_Data *
329 _eo_table_data_table_new(Efl_Id_Domain domain)
330 {
331    Eo_Id_Table_Data *tdata;
332
333    tdata = calloc(1, sizeof(Eo_Id_Table_Data));
334    if (!tdata) return NULL;
335    if (domain == EFL_ID_DOMAIN_SHARED)
336      {
337         if (!eina_lock_recursive_new(&(tdata->obj_lock)))
338           {
339              free(tdata);
340              return NULL;
341           }
342         tdata->shared = EINA_TRUE;
343      }
344    tdata->generation = rand() % MAX_GENERATIONS;
345    return tdata;
346 }
347
348 static inline Eo_Id_Data *
349 _eo_table_data_new(Efl_Id_Domain domain)
350 {
351    Eo_Id_Data *data;
352
353    data = calloc(1, sizeof(Eo_Id_Data));
354    if (!data) return NULL;
355    data->local_domain = domain;
356    data->domain_stack[data->stack_top] = data->local_domain;
357    data->tables[data->local_domain] =
358      _eo_table_data_table_new(data->local_domain);
359    if (domain != EFL_ID_DOMAIN_SHARED)
360      data->tables[EFL_ID_DOMAIN_SHARED] = _eo_table_data_shared_data;
361    return data;
362 }
363
364 static void
365 _eo_table_data_table_free(Eo_Id_Table_Data *tdata)
366 {
367    if (tdata->shared) eina_lock_free(&(tdata->obj_lock));
368    free(tdata);
369 }
370
371 static inline Eo_Id_Data *
372 _eo_table_data_get(void)
373 {
374    /* TIZEN_ONLY(20180518): Turn thread-validation off for unstable Tizen 5.0
375       apps which are violating thread-safety. Eo has gotten more strict-check
376       for thread violiation API calls. Since Tizen 5.0, many apps(which
377       violates EAPI thread safety rules) unexpectedly became unstable cause of
378       thread-validation. We disable this function for now but wish to remove
379       this temporary code in the near future when apps are ready to go. */
380 #ifdef DISABLE_EO_THREAD_CHECK
381    if (_tizen_eo_table_data) return _tizen_eo_table_data;
382    _tizen_eo_table_data = _eo_table_data_new(EFL_ID_DOMAIN_MAIN);
383    return _tizen_eo_table_data;
384 #else
385    Eo_Id_Data *data = eina_tls_get(_eo_table_data);
386    if (EINA_LIKELY(data != NULL)) return data;
387
388    data = _eo_table_data_new(EFL_ID_DOMAIN_THREAD);
389    if (!data) return NULL;
390
391    eina_tls_set(_eo_table_data, data);
392    return data;
393 #endif
394 }
395
396 static inline Eo_Id_Table_Data *
397 _eo_table_data_current_table_get(Eo_Id_Data *data)
398 {
399    return data->tables[data->domain_stack[data->stack_top]];
400 }
401
402 static inline Eo_Id_Table_Data *
403 _eo_table_data_table_get(Eo_Id_Data *data, Efl_Id_Domain domain)
404 {
405    return data->tables[domain];
406 }
407
408 static inline Eina_Bool
409 _eo_id_domain_compatible(const Eo *o1, const Eo *o2)
410 {
411    Efl_Id_Domain domain1 = ((Eo_Id)o1 >> SHIFT_DOMAIN) & MASK_DOMAIN;
412    Efl_Id_Domain domain2 = ((Eo_Id)o2 >> SHIFT_DOMAIN) & MASK_DOMAIN;
413    if (domain1 == domain2) return EINA_TRUE;
414    ERR("Object %p and %p are not compatible. Domain %i and %i do not match",
415        o1, o2, domain1, domain2);
416    return EINA_FALSE;
417 }
418
419 static inline void
420 _eo_obj_pointer_done(const Eo_Id obj_id)
421 {
422    Efl_Id_Domain domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
423    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return;
424    eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
425 }
426
427 //////////////////////////////////////////////////////////////////////////
428
429
430 /* Macro used to compose an Eo id */
431 #define EO_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE)                      \
432    (((Eo_Id) 0x1 << REF_TAG_SHIFT)                                 | \
433     ((Eo_Id)(MID_TABLE & MASK_MID_TABLE_ID) << SHIFT_MID_TABLE_ID) | \
434     ((Eo_Id)(TABLE & MASK_TABLE_ID) << SHIFT_TABLE_ID))
435
436 #define EO_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, DOMAIN, GENERATION)  \
437     (PARTIAL_ID                                                   | \
438      (((Eo_Id)DOMAIN & MASK_DOMAIN) << SHIFT_DOMAIN)              | \
439      ((ENTRY & MASK_ENTRY_ID) << SHIFT_ENTRY_ID)                  | \
440      (GENERATION & MASK_GENERATIONS))
441
442 /* Macro to extract from an Eo id the indexes of the tables */
443 #define EO_DECOMPOSE_ID(ID, MID_TABLE, TABLE, ENTRY, GENERATION) \
444    MID_TABLE = (ID >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;   \
445    TABLE = (ID >> SHIFT_TABLE_ID) & MASK_TABLE_ID;               \
446    ENTRY = (ID >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;               \
447    GENERATION = ID & MASK_GENERATIONS;
448
449 /* Macro used for readability */
450 #define TABLE_FROM_IDS tdata->eo_ids_tables[mid_table_id][table_id]
451
452 static inline _Eo_Id_Entry *
453 _get_available_entry(_Eo_Ids_Table *table)
454 {
455    _Eo_Id_Entry *entry = NULL;
456
457    if (table->start != MAX_ENTRY_ID)
458      {
459         /* Serve never used entries first */
460         entry = &(table->entries[table->start]);
461         UNPROTECT(table);
462         table->start++;
463         table->free_entries--;
464      }
465    else if (table->fifo_head != -1)
466      {
467         /* Pop a free entry from the fifo */
468         entry = &(table->entries[table->fifo_head]);
469         UNPROTECT(table);
470         if (entry->next_in_fifo == -1)
471           table->fifo_head = table->fifo_tail = -1;
472         else
473           table->fifo_head = entry->next_in_fifo;
474         table->free_entries--;
475      }
476
477    return entry;
478 }
479
480 static inline _Eo_Id_Entry *
481 _search_tables(Eo_Id_Table_Data *tdata)
482 {
483    _Eo_Ids_Table *table;
484    _Eo_Id_Entry *entry;
485
486    if (!tdata) return NULL;
487    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
488      {
489         if (!tdata->eo_ids_tables[mid_table_id])
490           {
491              /* Allocate a new intermediate table */
492              tdata->eo_ids_tables[mid_table_id] = _eo_id_mem_calloc(MAX_TABLE_ID, sizeof(_Eo_Ids_Table*));
493           }
494
495         for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
496           {
497              table = TABLE_FROM_IDS;
498
499              if (!table)
500                {
501                   if (tdata->empty_table)
502                     {
503                        /* Recycle the available empty table */
504                        table = tdata->empty_table;
505                        tdata->empty_table = NULL;
506                        UNPROTECT(table);
507                     }
508                   else
509                     {
510                        /* Allocate a new table */
511                        table = _eo_id_mem_calloc(1, sizeof(_Eo_Ids_Table));
512                     }
513                   /* Initialize the table and reserve the first entry */
514                   table->start = 1;
515                   table->free_entries = MAX_ENTRY_ID - 1;
516                   table->fifo_head = table->fifo_tail = -1;
517                   table->partial_id = EO_COMPOSE_PARTIAL_ID(mid_table_id, table_id);
518                   entry = &(table->entries[0]);
519                   UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
520                   TABLE_FROM_IDS = table;
521                   PROTECT(tdata->eo_ids_tables[mid_table_id]);
522                }
523              else
524                entry = _get_available_entry(table);
525
526              if (entry)
527                {
528                   /* Store table info into current table */
529                   tdata->current_table = table;
530                   return entry;
531                }
532           }
533      }
534
535    ERR("no more available entries to store eo objects");
536    tdata->current_table = NULL;
537    return NULL;
538 }
539
540 /* Gives a fake id that serves as a marker if eo id is off. */
541 static inline Eo_Id
542 _eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id)
543 {
544    _Eo_Id_Entry *entry = NULL;
545    Eo_Id_Data *data;
546    Eo_Id_Table_Data *tdata;
547    Eo_Id id;
548
549    data = _eo_table_data_get();
550    if (parent_id)
551      {
552         Efl_Id_Domain domain = ((Eo_Id)parent_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
553         tdata = _eo_table_data_table_get(data, domain);
554      }
555    else tdata = _eo_table_data_current_table_get(data);
556    if (!tdata) return 0;
557
558    if (EINA_LIKELY(!tdata->shared))
559      {
560         if (tdata->current_table)
561           entry = _get_available_entry(tdata->current_table);
562
563         if (!entry) entry = _search_tables(tdata);
564
565         if (!tdata->current_table || !entry)
566           {
567              return 0;
568           }
569
570         UNPROTECT(tdata->current_table);
571         /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
572         tdata->generation++;
573         if (tdata->generation >= MAX_GENERATIONS) tdata->generation = 1;
574         /* Fill the entry and return it's Eo Id */
575         entry->ptr = (_Eo_Object *)obj;
576         entry->active = 1;
577         entry->generation = tdata->generation;
578         PROTECT(tdata->current_table);
579         id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
580                                  (entry - tdata->current_table->entries),
581                                  data->domain_stack[data->stack_top],
582                                  entry->generation);
583      }
584    else
585      {
586         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
587         if (tdata->current_table)
588           entry = _get_available_entry(tdata->current_table);
589
590         if (!entry) entry = _search_tables(tdata);
591
592         if (!tdata->current_table || !entry)
593           {
594              id = 0;
595              goto shared_err;
596           }
597
598         UNPROTECT(tdata->current_table);
599         /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
600         tdata->generation++;
601         if (tdata->generation == MAX_GENERATIONS) tdata->generation = 1;
602         /* Fill the entry and return it's Eo Id */
603         entry->ptr = (_Eo_Object *)obj;
604         entry->active = 1;
605         entry->generation = tdata->generation;
606         PROTECT(tdata->current_table);
607         id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
608                                  (entry - tdata->current_table->entries),
609                                  EFL_ID_DOMAIN_SHARED,
610                                  entry->generation);
611 shared_err:
612         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
613      }
614    return id;
615 }
616
617 static inline void
618 _eo_id_release(const Eo_Id obj_id)
619 {
620    _Eo_Ids_Table *table;
621    _Eo_Id_Entry *entry;
622    Generation_Counter generation;
623    Table_Index mid_table_id, table_id, entry_id;
624    Efl_Id_Domain domain;
625    Eo_Id_Data *data;
626    Eo_Id_Table_Data *tdata;
627
628    domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
629    data = _eo_table_data_get();
630    tdata = _eo_table_data_table_get(data, domain);
631    if (!tdata) return;
632
633    EO_DECOMPOSE_ID(obj_id, mid_table_id, table_id, entry_id, generation);
634
635    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
636      {
637         // Check the validity of the entry
638         if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
639           {
640              entry = &(table->entries[entry_id]);
641              if (entry && entry->active && (entry->generation == generation))
642                {
643                   UNPROTECT(table);
644                   table->free_entries++;
645                   // Disable the entry
646                   entry->active = 0;
647                   entry->next_in_fifo = -1;
648                   // Push the entry into the fifo
649                   if (table->fifo_tail == -1)
650                     table->fifo_head = table->fifo_tail = entry_id;
651                   else
652                     {
653                        table->entries[table->fifo_tail].next_in_fifo = entry_id;
654                        table->fifo_tail = entry_id;
655                     }
656                   PROTECT(table);
657                   if (table->free_entries == MAX_ENTRY_ID)
658                     {
659                        UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
660                        TABLE_FROM_IDS = NULL;
661                        PROTECT(tdata->eo_ids_tables[mid_table_id]);
662                        // Recycle or free the empty table
663                        if (!tdata->empty_table) tdata->empty_table = table;
664                        else _eo_id_mem_free(table);
665                        if (tdata->current_table == table)
666                          tdata->current_table = NULL;
667                     }
668                   // In case an object is destroyed, wipe out the cache
669                   if (tdata->cache.id == obj_id)
670                     {
671                        tdata->cache.id = 0;
672                        tdata->cache.object = NULL;
673                     }
674                   if ((Eo_Id)tdata->cache.isa_id == obj_id)
675                     {
676                        tdata->cache.isa_id = NULL;
677                        tdata->cache.klass = NULL;;
678                        tdata->cache.isa = EINA_FALSE;
679                     }
680                   return;
681                }
682           }
683      }
684    else
685      {
686         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
687         // Check the validity of the entry
688         if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
689           {
690              entry = &(table->entries[entry_id]);
691              if (entry && entry->active && (entry->generation == generation))
692                {
693                   UNPROTECT(table);
694                   table->free_entries++;
695                   // Disable the entry
696                   entry->active = 0;
697                   entry->next_in_fifo = -1;
698                   // Push the entry into the fifo
699                   if (table->fifo_tail == -1)
700                     table->fifo_head = table->fifo_tail = entry_id;
701                   else
702                     {
703                        table->entries[table->fifo_tail].next_in_fifo = entry_id;
704                        table->fifo_tail = entry_id;
705                     }
706                   PROTECT(table);
707                   if (table->free_entries == MAX_ENTRY_ID)
708                     {
709                        UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
710                        TABLE_FROM_IDS = NULL;
711                        PROTECT(tdata->eo_ids_tables[mid_table_id]);
712                        // Recycle or free the empty table
713                        if (!tdata->empty_table) tdata->empty_table = table;
714                        else _eo_id_mem_free(table);
715                        if (tdata->current_table == table)
716                          tdata->current_table = NULL;
717                     }
718                   // In case an object is destroyed, wipe out the cache
719                   if (tdata->cache.id == obj_id)
720                     {
721                        tdata->cache.id = 0;
722                        tdata->cache.object = NULL;
723                     }
724                   if ((Eo_Id)tdata->cache.isa_id == obj_id)
725                     {
726                        tdata->cache.isa_id = NULL;
727                        tdata->cache.klass = NULL;;
728                        tdata->cache.isa = EINA_FALSE;
729                     }
730                   eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
731                   return;
732                }
733           }
734         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
735      }
736    ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.", (void *)obj_id);
737 }
738
739 static inline void
740 _eo_free_ids_tables(Eo_Id_Data *data)
741 {
742    Eo_Id_Table_Data *tdata;
743
744    if (!data) return;
745    tdata = data->tables[data->local_domain];
746    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
747      {
748         if (tdata->eo_ids_tables[mid_table_id])
749           {
750              for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
751                {
752                   if (TABLE_FROM_IDS)
753                     {
754                        _eo_id_mem_free(TABLE_FROM_IDS);
755                     }
756                }
757              _eo_id_mem_free(tdata->eo_ids_tables[mid_table_id]);
758           }
759         tdata->eo_ids_tables[mid_table_id] = NULL;
760      }
761    if (tdata->empty_table) _eo_id_mem_free(tdata->empty_table);
762    tdata->empty_table = tdata->current_table = NULL;
763    _eo_table_data_table_free(tdata);
764    data->tables[data->local_domain] = NULL;
765    free(data);
766 }
767
768 #ifdef EFL_DEBUG
769 static inline void
770 _eo_print(Eo_Id_Table_Data *tdata)
771 {
772    _Eo_Id_Entry *entry;
773    unsigned long obj_number = 0;
774
775    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
776      {
777         if (tdata->eo_ids_tables[mid_table_id])
778           {
779              for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
780                {
781                   if (TABLE_FROM_IDS)
782                     {
783                        for (Table_Index entry_id = 0; entry_id < MAX_ENTRY_ID; entry_id++)
784                          {
785                             entry = &(TABLE_FROM_IDS->entries[entry_id]);
786                             if (entry->active)
787                               {
788                                  printf("%ld: %p -> (%p, %p, %p, %p)\n", obj_number++,
789                                        entry->ptr,
790                                        (void *)mid_table_id, (void *)table_id, (void *)entry_id,
791                                        (void *)entry->generation);
792                               }
793                          }
794                     }
795                }
796           }
797      }
798 }
799 #endif