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