d6affe0cc6ae0640e01d5131690df2b0d5d0722b
[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 static inline Eo_Id_Table_Data *
324 _eo_table_data_table_new(Efl_Id_Domain domain)
325 {
326    Eo_Id_Table_Data *tdata;
327
328    tdata = calloc(1, sizeof(Eo_Id_Table_Data));
329    if (!tdata) return NULL;
330    if (domain == EFL_ID_DOMAIN_SHARED)
331      {
332         if (!eina_lock_recursive_new(&(tdata->obj_lock)))
333           {
334              free(tdata);
335              return NULL;
336           }
337         tdata->shared = EINA_TRUE;
338      }
339    tdata->generation = rand() % MAX_GENERATIONS;
340    return tdata;
341 }
342
343 static inline Eo_Id_Data *
344 _eo_table_data_new(Efl_Id_Domain domain)
345 {
346    Eo_Id_Data *data;
347
348    data = calloc(1, sizeof(Eo_Id_Data));
349    if (!data) return NULL;
350    data->local_domain = domain;
351    data->domain_stack[data->stack_top] = data->local_domain;
352    data->tables[data->local_domain] =
353      _eo_table_data_table_new(data->local_domain);
354    if (domain != EFL_ID_DOMAIN_SHARED)
355      data->tables[EFL_ID_DOMAIN_SHARED] = _eo_table_data_shared_data;
356    return data;
357 }
358
359 static void
360 _eo_table_data_table_free(Eo_Id_Table_Data *tdata)
361 {
362    if (tdata->shared) eina_lock_free(&(tdata->obj_lock));
363    free(tdata);
364 }
365
366 static inline Eo_Id_Data *
367 _eo_table_data_get(void)
368 {
369    Eo_Id_Data *data = eina_tls_get(_eo_table_data);
370    if (EINA_LIKELY(data != NULL)) return data;
371
372    data = _eo_table_data_new(EFL_ID_DOMAIN_THREAD);
373    if (!data) return NULL;
374
375    eina_tls_set(_eo_table_data, data);
376    return data;
377 }
378
379 static inline Eo_Id_Table_Data *
380 _eo_table_data_current_table_get(Eo_Id_Data *data)
381 {
382    return data->tables[data->domain_stack[data->stack_top]];
383 }
384
385 static inline Eo_Id_Table_Data *
386 _eo_table_data_table_get(Eo_Id_Data *data, Efl_Id_Domain domain)
387 {
388    return data->tables[domain];
389 }
390
391 static inline Eina_Bool
392 _eo_id_domain_compatible(const Eo *o1, const Eo *o2)
393 {
394    Efl_Id_Domain domain1 = ((Eo_Id)o1 >> SHIFT_DOMAIN) & MASK_DOMAIN;
395    Efl_Id_Domain domain2 = ((Eo_Id)o2 >> SHIFT_DOMAIN) & MASK_DOMAIN;
396    if (domain1 == domain2) return EINA_TRUE;
397    ERR("Object %p and %p are not compatible. Domain %i and %i do not match",
398        o1, o2, domain1, domain2);
399    return EINA_FALSE;
400 }
401
402 static inline void
403 _eo_obj_pointer_done(const Eo_Id obj_id)
404 {
405    Efl_Id_Domain domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
406    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return;
407    eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
408 }
409
410 //////////////////////////////////////////////////////////////////////////
411
412
413 /* Macro used to compose an Eo id */
414 #define EO_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE)                      \
415    (((Eo_Id) 0x1 << REF_TAG_SHIFT)                                 | \
416     ((Eo_Id)(MID_TABLE & MASK_MID_TABLE_ID) << SHIFT_MID_TABLE_ID) | \
417     ((Eo_Id)(TABLE & MASK_TABLE_ID) << SHIFT_TABLE_ID))
418
419 #define EO_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, DOMAIN, GENERATION)  \
420     (PARTIAL_ID                                                   | \
421      (((Eo_Id)DOMAIN & MASK_DOMAIN) << SHIFT_DOMAIN)              | \
422      ((ENTRY & MASK_ENTRY_ID) << SHIFT_ENTRY_ID)                  | \
423      (GENERATION & MASK_GENERATIONS))
424
425 /* Macro to extract from an Eo id the indexes of the tables */
426 #define EO_DECOMPOSE_ID(ID, MID_TABLE, TABLE, ENTRY, GENERATION) \
427    MID_TABLE = (ID >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;   \
428    TABLE = (ID >> SHIFT_TABLE_ID) & MASK_TABLE_ID;               \
429    ENTRY = (ID >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;               \
430    GENERATION = ID & MASK_GENERATIONS;
431
432 /* Macro used for readability */
433 #define TABLE_FROM_IDS tdata->eo_ids_tables[mid_table_id][table_id]
434
435 static inline _Eo_Id_Entry *
436 _get_available_entry(_Eo_Ids_Table *table)
437 {
438    _Eo_Id_Entry *entry = NULL;
439
440    if (table->start != MAX_ENTRY_ID)
441      {
442         /* Serve never used entries first */
443         entry = &(table->entries[table->start]);
444         UNPROTECT(table);
445         table->start++;
446         table->free_entries--;
447      }
448    else if (table->fifo_head != -1)
449      {
450         /* Pop a free entry from the fifo */
451         entry = &(table->entries[table->fifo_head]);
452         UNPROTECT(table);
453         if (entry->next_in_fifo == -1)
454           table->fifo_head = table->fifo_tail = -1;
455         else
456           table->fifo_head = entry->next_in_fifo;
457         table->free_entries--;
458      }
459
460    return entry;
461 }
462
463 static inline _Eo_Id_Entry *
464 _search_tables(Eo_Id_Table_Data *tdata)
465 {
466    _Eo_Ids_Table *table;
467    _Eo_Id_Entry *entry;
468
469    if (!tdata) return NULL;
470    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
471      {
472         if (!tdata->eo_ids_tables[mid_table_id])
473           {
474              /* Allocate a new intermediate table */
475              tdata->eo_ids_tables[mid_table_id] = _eo_id_mem_calloc(MAX_TABLE_ID, sizeof(_Eo_Ids_Table*));
476           }
477
478         for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
479           {
480              table = TABLE_FROM_IDS;
481
482              if (!table)
483                {
484                   if (tdata->empty_table)
485                     {
486                        /* Recycle the available empty table */
487                        table = tdata->empty_table;
488                        tdata->empty_table = NULL;
489                        UNPROTECT(table);
490                     }
491                   else
492                     {
493                        /* Allocate a new table */
494                        table = _eo_id_mem_calloc(1, sizeof(_Eo_Ids_Table));
495                     }
496                   /* Initialize the table and reserve the first entry */
497                   table->start = 1;
498                   table->free_entries = MAX_ENTRY_ID - 1;
499                   table->fifo_head = table->fifo_tail = -1;
500                   table->partial_id = EO_COMPOSE_PARTIAL_ID(mid_table_id, table_id);
501                   entry = &(table->entries[0]);
502                   UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
503                   TABLE_FROM_IDS = table;
504                   PROTECT(tdata->eo_ids_tables[mid_table_id]);
505                }
506              else
507                entry = _get_available_entry(table);
508
509              if (entry)
510                {
511                   /* Store table info into current table */
512                   tdata->current_table = table;
513                   return entry;
514                }
515           }
516      }
517
518    ERR("no more available entries to store eo objects");
519    tdata->current_table = NULL;
520    return NULL;
521 }
522
523 /* Gives a fake id that serves as a marker if eo id is off. */
524 static inline Eo_Id
525 _eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id)
526 {
527    _Eo_Id_Entry *entry = NULL;
528    Eo_Id_Data *data;
529    Eo_Id_Table_Data *tdata;
530    Eo_Id id;
531
532    data = _eo_table_data_get();
533    if (parent_id)
534      {
535         Efl_Id_Domain domain = ((Eo_Id)parent_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
536         tdata = _eo_table_data_table_get(data, domain);
537      }
538    else tdata = _eo_table_data_current_table_get(data);
539    if (!tdata) return 0;
540
541    if (EINA_LIKELY(!tdata->shared))
542      {
543         if (tdata->current_table)
544           entry = _get_available_entry(tdata->current_table);
545
546         if (!entry) entry = _search_tables(tdata);
547
548         if (!tdata->current_table || !entry)
549           {
550              return 0;
551           }
552
553         UNPROTECT(tdata->current_table);
554         /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
555         tdata->generation++;
556         if (tdata->generation >= MAX_GENERATIONS) tdata->generation = 1;
557         /* Fill the entry and return it's Eo Id */
558         entry->ptr = (_Eo_Object *)obj;
559         entry->active = 1;
560         entry->generation = tdata->generation;
561         PROTECT(tdata->current_table);
562         id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
563                                  (entry - tdata->current_table->entries),
564                                  data->domain_stack[data->stack_top],
565                                  entry->generation);
566      }
567    else
568      {
569         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
570         if (tdata->current_table)
571           entry = _get_available_entry(tdata->current_table);
572
573         if (!entry) entry = _search_tables(tdata);
574
575         if (!tdata->current_table || !entry)
576           {
577              id = 0;
578              goto shared_err;
579           }
580
581         UNPROTECT(tdata->current_table);
582         /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
583         tdata->generation++;
584         if (tdata->generation == MAX_GENERATIONS) tdata->generation = 1;
585         /* Fill the entry and return it's Eo Id */
586         entry->ptr = (_Eo_Object *)obj;
587         entry->active = 1;
588         entry->generation = tdata->generation;
589         PROTECT(tdata->current_table);
590         id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
591                                  (entry - tdata->current_table->entries),
592                                  EFL_ID_DOMAIN_SHARED,
593                                  entry->generation);
594 shared_err:
595         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
596      }
597    return id;
598 }
599
600 static inline void
601 _eo_id_release(const Eo_Id obj_id)
602 {
603    _Eo_Ids_Table *table;
604    _Eo_Id_Entry *entry;
605    Generation_Counter generation;
606    Table_Index mid_table_id, table_id, entry_id;
607    Efl_Id_Domain domain;
608    Eo_Id_Data *data;
609    Eo_Id_Table_Data *tdata;
610
611    domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
612    data = _eo_table_data_get();
613    tdata = _eo_table_data_table_get(data, domain);
614    if (!tdata) return;
615
616    EO_DECOMPOSE_ID(obj_id, mid_table_id, table_id, entry_id, generation);
617
618    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
619      {
620         // Check the validity of the entry
621         if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
622           {
623              entry = &(table->entries[entry_id]);
624              if (entry && entry->active && (entry->generation == generation))
625                {
626                   UNPROTECT(table);
627                   table->free_entries++;
628                   // Disable the entry
629                   entry->active = 0;
630                   entry->next_in_fifo = -1;
631                   // Push the entry into the fifo
632                   if (table->fifo_tail == -1)
633                     table->fifo_head = table->fifo_tail = entry_id;
634                   else
635                     {
636                        table->entries[table->fifo_tail].next_in_fifo = entry_id;
637                        table->fifo_tail = entry_id;
638                     }
639                   PROTECT(table);
640                   if (table->free_entries == MAX_ENTRY_ID)
641                     {
642                        UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
643                        TABLE_FROM_IDS = NULL;
644                        PROTECT(tdata->eo_ids_tables[mid_table_id]);
645                        // Recycle or free the empty table
646                        if (!tdata->empty_table) tdata->empty_table = table;
647                        else _eo_id_mem_free(table);
648                        if (tdata->current_table == table)
649                          tdata->current_table = NULL;
650                     }
651                   // In case an object is destroyed, wipe out the cache
652                   if (tdata->cache.id == obj_id)
653                     {
654                        tdata->cache.id = 0;
655                        tdata->cache.object = NULL;
656                     }
657                   if ((Eo_Id)tdata->cache.isa_id == obj_id)
658                     {
659                        tdata->cache.isa_id = NULL;
660                        tdata->cache.klass = NULL;;
661                        tdata->cache.isa = EINA_FALSE;
662                     }
663                   return;
664                }
665           }
666      }
667    else
668      {
669         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
670         // Check the validity of the entry
671         if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
672           {
673              entry = &(table->entries[entry_id]);
674              if (entry && entry->active && (entry->generation == generation))
675                {
676                   UNPROTECT(table);
677                   table->free_entries++;
678                   // Disable the entry
679                   entry->active = 0;
680                   entry->next_in_fifo = -1;
681                   // Push the entry into the fifo
682                   if (table->fifo_tail == -1)
683                     table->fifo_head = table->fifo_tail = entry_id;
684                   else
685                     {
686                        table->entries[table->fifo_tail].next_in_fifo = entry_id;
687                        table->fifo_tail = entry_id;
688                     }
689                   PROTECT(table);
690                   if (table->free_entries == MAX_ENTRY_ID)
691                     {
692                        UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
693                        TABLE_FROM_IDS = NULL;
694                        PROTECT(tdata->eo_ids_tables[mid_table_id]);
695                        // Recycle or free the empty table
696                        if (!tdata->empty_table) tdata->empty_table = table;
697                        else _eo_id_mem_free(table);
698                        if (tdata->current_table == table)
699                          tdata->current_table = NULL;
700                     }
701                   // In case an object is destroyed, wipe out the cache
702                   if (tdata->cache.id == obj_id)
703                     {
704                        tdata->cache.id = 0;
705                        tdata->cache.object = NULL;
706                     }
707                   if ((Eo_Id)tdata->cache.isa_id == obj_id)
708                     {
709                        tdata->cache.isa_id = NULL;
710                        tdata->cache.klass = NULL;;
711                        tdata->cache.isa = EINA_FALSE;
712                     }
713                   eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
714                   return;
715                }
716           }
717         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
718      }
719    ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.", (void *)obj_id);
720 }
721
722 static inline void
723 _eo_free_ids_tables(Eo_Id_Data *data)
724 {
725    Eo_Id_Table_Data *tdata;
726
727    if (!data) return;
728    tdata = data->tables[data->local_domain];
729    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
730      {
731         if (tdata->eo_ids_tables[mid_table_id])
732           {
733              for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
734                {
735                   if (TABLE_FROM_IDS)
736                     {
737                        _eo_id_mem_free(TABLE_FROM_IDS);
738                     }
739                }
740              _eo_id_mem_free(tdata->eo_ids_tables[mid_table_id]);
741           }
742         tdata->eo_ids_tables[mid_table_id] = NULL;
743      }
744    if (tdata->empty_table) _eo_id_mem_free(tdata->empty_table);
745    tdata->empty_table = tdata->current_table = NULL;
746    _eo_table_data_table_free(tdata);
747    data->tables[data->local_domain] = NULL;
748    free(data);
749 }
750
751 #ifdef EFL_DEBUG
752 static inline void
753 _eo_print(Eo_Id_Table_Data *tdata)
754 {
755    _Eo_Id_Entry *entry;
756    unsigned long obj_number = 0;
757
758    for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
759      {
760         if (tdata->eo_ids_tables[mid_table_id])
761           {
762              for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
763                {
764                   if (TABLE_FROM_IDS)
765                     {
766                        for (Table_Index entry_id = 0; entry_id < MAX_ENTRY_ID; entry_id++)
767                          {
768                             entry = &(TABLE_FROM_IDS->entries[entry_id]);
769                             if (entry->active)
770                               {
771                                  printf("%ld: %p -> (%p, %p, %p, %p)\n", obj_number++,
772                                        entry->ptr,
773                                        (void *)mid_table_id, (void *)table_id, (void *)entry_id,
774                                        (void *)entry->generation);
775                               }
776                          }
777                     }
778                }
779           }
780      }
781 }
782 #endif