summaryrefslogblamecommitdiff
path: root/src/lib/eo/eo_ptr_indirection.x
blob: d9bd863cc7c46584c4f1ec7f614dc290bb3bcb64 (plain) (tree)
1
2
                   
                




























































                                                                                     
                                   
                                   
                                   
                                   
                                   







                                    
                                   
                                   
                                   
                                   
                                   






                                           

                                                                       






                                                                               
                                                





                                                                      
                                              



                                                        
                                                              
 




                                                                           












                                        
                





                                                                             
                                             


























                                                                                          
                

















                                                                  
                 

















                                                                            

                                                      



                              
                   

























                                                                   
                                                                          
 

                                                  
 

                        









                                 





                                                       

                                                                             

                                                                          
                                              
                              








                                                                    
 


                                                    
 



                                              
 



                                               

                                                         


                         

                                  
                                                

                
 











                                                            
                                                                     

               
 


                                                  
                                                         







                                                   
 

                                                   
 





                                                  
 

                                                            
 




                                                                
 




                                                                     



                                                                            
 
 


                                        


                                                                 

 
                                                                          
 
 




                                                                      
 



                                                                     
                                     
 





                                                                  
 

                                                                   





























                                                    
                                       



                        
                           

                                                                                      
                                                

                                                    
                                                                                                          







                                                                           
                                         

                                                              

                                                  












                                                                                    
                                                                
                                         
                                                              






                                                           
                                               





                                                        
                               


               
                                                              
                   
                                                           
 
                              


                           
 

                               
      

                                                                                
      

                                                       
 
                                   
      












                                                                  
                                                                        








                                                                         
      

       
                                                                

                                                             
 


















                                                                         
                                                      

                                                    
                                                                   

             




                                  



                                                








                                                   

                                                                         
                                                   
      

                                                                           
           

                                                                             
                







                                                                   
                      




























                                                                                
                



           
                                                                




                                                                             
                




































                                                                                
                                                                             
                         
                
           
                                                                   
      
                                                                                                        


                  
                                     
 



                                            

                                                                                      
                                               







                                                                                
                                                                 
           
                                                  
      




                                                               



                  
                                  


                                
 

                                                                                      
                                               





















                                                                                                
#include <assert.h>
#ifdef HAVE_MMAP
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#endif

/* Start of pointer indirection:
 *
 * This feature is responsible of hiding from the developer the real pointer of
 * the Eo object to supply a better memory management by preventing bad usage
 * of the pointers.
 *
 * Eo * is no more a pointer but indexes to an entry into an ids table.
 * For a better memory usage:
 * - a tree structure is used, composed of a top level table pointing at
 *   mid tables pointing at tables composed of entries.
 * - tables are allocated when needed (i.e no more empty entries in allocated tables.
 * - empty tables are freed, except one kept as spare table.
 *
 * An Eo id is contructed by bits manipulation of table indexes and a generation.
 *
 * id = Mid Table | Table | Entry | Generation
 *
 * Generation helps finding abuse of ids. When an entry is assigned to an
 * object, a generation is inserted into the id. If the developer uses this id
 * although the object is freed and another one has replaced it into the same
 * entry of the table, the generation will be different and an error will
 * occur when accessing with the old id.
 *
 * Each Table is composed of:
 * - an index 'start' indicating which free entry is the next one to use.
 * - 2 indexes 'fifo_head' and 'fifo_tail' defining a fifo,
 *   that will help us to store the entries to be reused. It stores only the
 *   entries that have been used at least one time. The entries that have
 *   never been used are "pointed" by the start parameter.
 * - entries composed of:
 *    - a pointer to the object
 *    - an index 'next_in_fifo' used to chain the free entries in the fifo
 *    - a flag indicating if the entry is active
 *    - a generation assigned to the object
 *
 * When an entry is searched into a table, we first use one of the entries that
 * has never been used. If there is none, we try to pop from the fifo.
 * If a such entry doesn't exist, we pass to the next table.
 * When an entry is found, we reserve it to the object pointer
 * then contruct and return the related Eo id.
 *
 * Assigning all the entries of a table before trying to reuse them from
 * the fifo ensures that we are not going to soon recycle a released entry,
 * thus minimize the risks of an aggressive del() then use() on a single entry.
 *
 * The indexes and a reference to the last table which served an entry is kept
 * and is reused prior to the others untill it is full.
 * When an object is freed, the entry into the table is released by appending
 * it to the fifo.
 */

#if SIZEOF_UINTPTR_T == 4
/* 32 bits */
# define BITS_MID_TABLE_ID        5
# define BITS_TABLE_ID            5
# define BITS_ENTRY_ID           11
# define BITS_GENERATION_COUNTER  7
# define BITS_DOMAIN              2
# define BITS_CLASS               1
# define REF_TAG_SHIFT           31
# define DROPPED_TABLES           0
# define DROPPED_ENTRIES          4
typedef int16_t Table_Index;
typedef uint16_t Generation_Counter;
#else
/* 64 bits */
# define BITS_MID_TABLE_ID       11
# define BITS_TABLE_ID           11
# define BITS_ENTRY_ID           11
# define BITS_GENERATION_COUNTER 27
# define BITS_DOMAIN              2
# define BITS_CLASS               1
# define REF_TAG_SHIFT           63
# define DROPPED_TABLES           2
# define DROPPED_ENTRIES          3
typedef int16_t Table_Index;
typedef uint32_t Generation_Counter;
#endif

/* Shifts macros to manipulate the Eo id */
#define SHIFT_DOMAIN          (BITS_MID_TABLE_ID + BITS_TABLE_ID + \
                               BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
#define SHIFT_MID_TABLE_ID    (BITS_TABLE_ID + \
                               BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
#define SHIFT_TABLE_ID        (BITS_ENTRY_ID + BITS_GENERATION_COUNTER)
#define SHIFT_ENTRY_ID        (BITS_GENERATION_COUNTER)

/* Maximum ranges - a few tables and entries are dropped to minimize the amount
 * of wasted bytes, see _eo_id_mem_alloc */
#define MAX_DOMAIN            (1 << BITS_DOMAIN)
#define MAX_MID_TABLE_ID      (1 << BITS_MID_TABLE_ID)
#define MAX_TABLE_ID          ((1 << BITS_TABLE_ID) - DROPPED_TABLES )
#define MAX_ENTRY_ID          ((1 << BITS_ENTRY_ID) - DROPPED_ENTRIES)
#define MAX_GENERATIONS       (1 << BITS_GENERATION_COUNTER)

/* Masks */
#define MASK_DOMAIN           (MAX_DOMAIN - 1)
#define MASK_MID_TABLE_ID     (MAX_MID_TABLE_ID - 1)
#define MASK_TABLE_ID         ((1 << BITS_TABLE_ID) - 1)
#define MASK_ENTRY_ID         ((1 << BITS_ENTRY_ID) - 1)
#define MASK_GENERATIONS      (MAX_GENERATIONS - 1)
#define MASK_OBJ_TAG          (((Eo_Id) 1) << (REF_TAG_SHIFT))

/* This only applies to classes. Used to artificially enlarge the class ids
 * to reduce the likelihood of a clash with normal integers. */
#define CLASS_TAG_SHIFT       (REF_TAG_SHIFT - 1)
#define MASK_CLASS_TAG        (((Eo_Id) 1) << (CLASS_TAG_SHIFT))

#define MEM_HEADER_SIZE       16
#define MEM_PAGE_SIZE         4096
#define MEM_MAGIC             0x3f61ec8a

typedef struct _Mem_Header
{
   size_t size;
   size_t magic;
} Mem_Header;

static void *
_eo_id_mem_alloc(size_t size)
{
#ifdef HAVE_MMAP
   void *ptr;
   Mem_Header *hdr;
   size_t newsize;
   newsize = MEM_PAGE_SIZE * ((size + MEM_HEADER_SIZE + MEM_PAGE_SIZE - 1) / 
                              MEM_PAGE_SIZE);
   ptr = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
              MAP_PRIVATE | MAP_ANON, -1, 0);
   if (ptr == MAP_FAILED)
     {
        ERR("mmap of eo id table region failed!");
        return NULL;
     }
   hdr = ptr;
   hdr->size = newsize;
   hdr->magic = MEM_MAGIC;
   /* DBG("asked:%lu allocated:%lu wasted:%lu bytes", size, newsize, (newsize - size)); */
   return (void *)(((unsigned char *)ptr) + MEM_HEADER_SIZE);
#else
   return malloc(size);
#endif
}

static void *
_eo_id_mem_calloc(size_t num, size_t size)
{
   void *ptr = _eo_id_mem_alloc(num * size);
   if (!ptr) return NULL;
   memset(ptr, 0, num * size);
   return ptr;
}

static void
_eo_id_mem_free(void *ptr)
{
#ifdef HAVE_MMAP
   Mem_Header *hdr;
   if (!ptr) return;
   hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
   if (hdr->magic != MEM_MAGIC)
     {
        ERR("unmap of eo table region has bad magic!");
        return;
     }
   munmap(hdr, hdr->size);
#else
   free(ptr);
#endif
}

#ifdef EINA_DEBUG_MALLOC
static void
_eo_id_mem_protect(void *ptr, Eina_Bool may_not_write)
{
# ifdef HAVE_MMAP
   Mem_Header *hdr;
   if (!ptr) return;
   hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
   if (hdr->magic != MEM_MAGIC)
     {
        ERR("mprotect of eo table region has bad magic!");
        return;
     }
   mprotect(hdr, hdr->size, PROT_READ | ( may_not_write ? 0 : PROT_WRITE) );
# endif
}
# define   PROTECT(_ptr_)   _eo_id_mem_protect((_ptr_), EINA_TRUE)
# define UNPROTECT(_ptr_)   _eo_id_mem_protect((_ptr_), EINA_FALSE)
#else
# define   PROTECT(_ptr_)
# define UNPROTECT(_ptr_)
#endif

#define EO_ALIGN_SIZE(size) eina_mempool_alignof(size)

/* Entry */
typedef struct
{
   /* Pointer to the object */
   _Eo_Object *ptr;
   /* Indicates where to find the next entry to recycle */
   Table_Index next_in_fifo;
   /* Active flag */
   unsigned int active     : 1;
   /* Generation */
   unsigned int generation : BITS_GENERATION_COUNTER;

} _Eo_Id_Entry;

/* Table */
typedef struct
{
   /* Indicates where start the "never used" entries */
   Table_Index start;
   /* Indicates where to find the next entry to recycle */
   Table_Index fifo_head;
   /* Indicates where to add an entry to recycle */
   Table_Index fifo_tail;
   /* Packed mid table and table indexes */
   Eo_Id partial_id;
   /* Counter of free entries */
   unsigned int free_entries;
   /* Entries of the table holding real pointers and generations */
   _Eo_Id_Entry entries[MAX_ENTRY_ID];
} _Eo_Ids_Table;

//////////////////////////////////////////////////////////////////////////

typedef struct _Eo_Id_Data       Eo_Id_Data;
typedef struct _Eo_Id_Table_Data Eo_Id_Table_Data;

struct _Eo_Id_Table_Data
{
   /* Cached eoid lookups */
   struct
     {
        Eo_Id             id;
        _Eo_Object       *object;
        const Eo         *isa_id;
        const Efl_Class  *klass;
        Eina_Bool         isa;
     }
   cache;
   /* Tables handling pointers indirection */
   _Eo_Ids_Table     **eo_ids_tables[MAX_MID_TABLE_ID];
   /* Current table used for following allocations */
   _Eo_Ids_Table      *current_table;
   /* Spare empty table */
   _Eo_Ids_Table      *empty_table;
   /* Optional lock around all objects in eoid table - only used if shared */
   Eina_Lock           obj_lock;
   /* Next generation to use when assigning a new entry to a Eo pointer */
   Generation_Counter  generation;
   /* are we shared so we need lock/unlock? */
   Eina_Bool           shared;
};

struct _Eo_Id_Data
{
   Eo_Id_Table_Data   *tables[4];
   unsigned char       local_domain;
   unsigned char       stack_top;
   unsigned char       domain_stack[255 - (sizeof(void *) * 4) - 2];
};

extern Eina_TLS          _eo_table_data;
extern Eo_Id_Data       *_eo_table_data_shared;
extern Eo_Id_Table_Data *_eo_table_data_shared_data;

static inline Eo_Id_Table_Data *
_eo_table_data_table_new(Efl_Id_Domain domain)
{
   Eo_Id_Table_Data *tdata;

   tdata = calloc(1, sizeof(Eo_Id_Table_Data));
   if (!tdata) return NULL;
   if (domain == EFL_ID_DOMAIN_SHARED)
     {
        if (!eina_lock_recursive_new(&(tdata->obj_lock)))
          {
             free(tdata);
             return NULL;
          }
        tdata->shared = EINA_TRUE;
     }
   tdata->generation = rand() % MAX_GENERATIONS;
   return tdata;
}

static inline Eo_Id_Data *
_eo_table_data_new(Efl_Id_Domain domain)
{
   Eo_Id_Data *data;

   data = calloc(1, sizeof(Eo_Id_Data));
   if (!data) return NULL;
   data->local_domain = domain;
   data->domain_stack[data->stack_top] = data->local_domain;
   data->tables[data->local_domain] =
     _eo_table_data_table_new(data->local_domain);
   if (domain != EFL_ID_DOMAIN_SHARED)
     data->tables[EFL_ID_DOMAIN_SHARED] = _eo_table_data_shared_data;
   return data;
}

static void
_eo_table_data_table_free(Eo_Id_Table_Data *tdata)
{
   if (tdata->shared) eina_lock_free(&(tdata->obj_lock));
   free(tdata);
}

static inline Eo_Id_Data *
_eo_table_data_get(void)
{
   Eo_Id_Data *data = eina_tls_get(_eo_table_data);
   if (EINA_LIKELY(data != NULL)) return data;

   data = _eo_table_data_new(EFL_ID_DOMAIN_THREAD);
   if (!data) return NULL;

   eina_tls_set(_eo_table_data, data);
   return data;
}

static inline Eo_Id_Table_Data *
_eo_table_data_current_table_get(Eo_Id_Data *data)
{
   return data->tables[data->domain_stack[data->stack_top]];
}

static inline Eo_Id_Table_Data *
_eo_table_data_table_get(Eo_Id_Data *data, Efl_Id_Domain domain)
{
   return data->tables[domain];
}

static inline Eina_Bool
_eo_id_domain_compatible(const Eo *o1, const Eo *o2)
{
   Efl_Id_Domain domain1 = ((Eo_Id)o1 >> SHIFT_DOMAIN) & MASK_DOMAIN;
   Efl_Id_Domain domain2 = ((Eo_Id)o2 >> SHIFT_DOMAIN) & MASK_DOMAIN;
   if (domain1 == domain2) return EINA_TRUE;
   ERR("Object %p and %p are not compatible. Domain %i and %i do not match",
       o1, o2, domain1, domain2);
   return EINA_FALSE;
}

static inline void
_eo_obj_pointer_done(const Eo_Id obj_id)
{
   Efl_Id_Domain domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
   if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return;
   eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
}

//////////////////////////////////////////////////////////////////////////


/* Macro used to compose an Eo id */
#define EO_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE)                      \
   (((Eo_Id) 0x1 << REF_TAG_SHIFT)                                 | \
    ((Eo_Id)(MID_TABLE & MASK_MID_TABLE_ID) << SHIFT_MID_TABLE_ID) | \
    ((Eo_Id)(TABLE & MASK_TABLE_ID) << SHIFT_TABLE_ID))

#define EO_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, DOMAIN, GENERATION)  \
    (PARTIAL_ID                                                   | \
     (((Eo_Id)DOMAIN & MASK_DOMAIN) << SHIFT_DOMAIN)              | \
     ((ENTRY & MASK_ENTRY_ID) << SHIFT_ENTRY_ID)                  | \
     (GENERATION & MASK_GENERATIONS))

/* Macro to extract from an Eo id the indexes of the tables */
#define EO_DECOMPOSE_ID(ID, MID_TABLE, TABLE, ENTRY, GENERATION) \
   MID_TABLE = (ID >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;   \
   TABLE = (ID >> SHIFT_TABLE_ID) & MASK_TABLE_ID;               \
   ENTRY = (ID >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;               \
   GENERATION = ID & MASK_GENERATIONS;

/* Macro used for readability */
#define TABLE_FROM_IDS tdata->eo_ids_tables[mid_table_id][table_id]

static inline _Eo_Id_Entry *
_get_available_entry(_Eo_Ids_Table *table)
{
   _Eo_Id_Entry *entry = NULL;

   if (table->start != MAX_ENTRY_ID)
     {
        /* Serve never used entries first */
        entry = &(table->entries[table->start]);
        UNPROTECT(table);
        table->start++;
        table->free_entries--;
     }
   else if (table->fifo_head != -1)
     {
        /* Pop a free entry from the fifo */
        entry = &(table->entries[table->fifo_head]);
        UNPROTECT(table);
        if (entry->next_in_fifo == -1)
          table->fifo_head = table->fifo_tail = -1;
        else
          table->fifo_head = entry->next_in_fifo;
        table->free_entries--;
     }

   return entry;
}

static inline _Eo_Id_Entry *
_search_tables(Eo_Id_Table_Data *tdata)
{
   _Eo_Ids_Table *table;
   _Eo_Id_Entry *entry;

   if (!tdata) return NULL;
   for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
     {
        if (!tdata->eo_ids_tables[mid_table_id])
          {
             /* Allocate a new intermediate table */
             tdata->eo_ids_tables[mid_table_id] = _eo_id_mem_calloc(MAX_TABLE_ID, sizeof(_Eo_Ids_Table*));
          }

        for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
          {
             table = TABLE_FROM_IDS;

             if (!table)
               {
                  if (tdata->empty_table)
                    {
                       /* Recycle the available empty table */
                       table = tdata->empty_table;
                       tdata->empty_table = NULL;
                       UNPROTECT(table);
                    }
                  else
                    {
                       /* Allocate a new table */
                       table = _eo_id_mem_calloc(1, sizeof(_Eo_Ids_Table));
                    }
                  /* Initialize the table and reserve the first entry */
                  table->start = 1;
                  table->free_entries = MAX_ENTRY_ID - 1;
                  table->fifo_head = table->fifo_tail = -1;
                  table->partial_id = EO_COMPOSE_PARTIAL_ID(mid_table_id, table_id);
                  entry = &(table->entries[0]);
                  UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
                  TABLE_FROM_IDS = table;
                  PROTECT(tdata->eo_ids_tables[mid_table_id]);
               }
             else
               entry = _get_available_entry(table);

             if (entry)
               {
                  /* Store table info into current table */
                  tdata->current_table = table;
                  return entry;
               }
          }
     }

   ERR("no more available entries to store eo objects");
   tdata->current_table = NULL;
   return NULL;
}

/* Gives a fake id that serves as a marker if eo id is off. */
static inline Eo_Id
_eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id)
{
   _Eo_Id_Entry *entry = NULL;
   Eo_Id_Data *data;
   Eo_Id_Table_Data *tdata;
   Eo_Id id;

   data = _eo_table_data_get();
   if (parent_id)
     {
        Efl_Id_Domain domain = ((Eo_Id)parent_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
        tdata = _eo_table_data_table_get(data, domain);
     }
   else tdata = _eo_table_data_current_table_get(data);
   if (!tdata) return 0;

   if (EINA_LIKELY(!tdata->shared))
     {
        if (tdata->current_table)
          entry = _get_available_entry(tdata->current_table);

        if (!entry) entry = _search_tables(tdata);

        if (!tdata->current_table || !entry)
          {
             return 0;
          }

        UNPROTECT(tdata->current_table);
        /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
        tdata->generation++;
        if (tdata->generation >= MAX_GENERATIONS) tdata->generation = 1;
        /* Fill the entry and return it's Eo Id */
        entry->ptr = (_Eo_Object *)obj;
        entry->active = 1;
        entry->generation = tdata->generation;
        PROTECT(tdata->current_table);
        id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
                                 (entry - tdata->current_table->entries),
                                 data->domain_stack[data->stack_top],
                                 entry->generation);
     }
   else
     {
        eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
        if (tdata->current_table)
          entry = _get_available_entry(tdata->current_table);

        if (!entry) entry = _search_tables(tdata);

        if (!tdata->current_table || !entry)
          {
             id = 0;
             goto shared_err;
          }

        UNPROTECT(tdata->current_table);
        /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
        tdata->generation++;
        if (tdata->generation == MAX_GENERATIONS) tdata->generation = 1;
        /* Fill the entry and return it's Eo Id */
        entry->ptr = (_Eo_Object *)obj;
        entry->active = 1;
        entry->generation = tdata->generation;
        PROTECT(tdata->current_table);
        id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
                                 (entry - tdata->current_table->entries),
                                 EFL_ID_DOMAIN_SHARED,
                                 entry->generation);
shared_err:
        eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
     }
   return id;
}

static inline void
_eo_id_release(const Eo_Id obj_id)
{
   _Eo_Ids_Table *table;
   _Eo_Id_Entry *entry;
   Generation_Counter generation;
   Table_Index mid_table_id, table_id, entry_id;
   Efl_Id_Domain domain;
   Eo_Id_Data *data;
   Eo_Id_Table_Data *tdata;

   domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
   data = _eo_table_data_get();
   tdata = _eo_table_data_table_get(data, domain);
   if (!tdata) return;

   EO_DECOMPOSE_ID(obj_id, mid_table_id, table_id, entry_id, generation);

   if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
     {
        // Check the validity of the entry
        if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
          {
             entry = &(table->entries[entry_id]);
             if (entry && entry->active && (entry->generation == generation))
               {
                  UNPROTECT(table);
                  table->free_entries++;
                  // Disable the entry
                  entry->active = 0;
                  entry->next_in_fifo = -1;
                  // Push the entry into the fifo
                  if (table->fifo_tail == -1)
                    table->fifo_head = table->fifo_tail = entry_id;
                  else
                    {
                       table->entries[table->fifo_tail].next_in_fifo = entry_id;
                       table->fifo_tail = entry_id;
                    }
                  PROTECT(table);
                  if (table->free_entries == MAX_ENTRY_ID)
                    {
                       UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
                       TABLE_FROM_IDS = NULL;
                       PROTECT(tdata->eo_ids_tables[mid_table_id]);
                       // Recycle or free the empty table
                       if (!tdata->empty_table) tdata->empty_table = table;
                       else _eo_id_mem_free(table);
                       if (tdata->current_table == table)
                         tdata->current_table = NULL;
                    }
                  // In case an object is destroyed, wipe out the cache
                  if (tdata->cache.id == obj_id)
                    {
                       tdata->cache.id = 0;
                       tdata->cache.object = NULL;
                    }
                  if ((Eo_Id)tdata->cache.isa_id == obj_id)
                    {
                       tdata->cache.isa_id = NULL;
                       tdata->cache.klass = NULL;;
                       tdata->cache.isa = EINA_FALSE;
                    }
                  return;
               }
          }
     }
   else
     {
        eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
        // Check the validity of the entry
        if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
          {
             entry = &(table->entries[entry_id]);
             if (entry && entry->active && (entry->generation == generation))
               {
                  UNPROTECT(table);
                  table->free_entries++;
                  // Disable the entry
                  entry->active = 0;
                  entry->next_in_fifo = -1;
                  // Push the entry into the fifo
                  if (table->fifo_tail == -1)
                    table->fifo_head = table->fifo_tail = entry_id;
                  else
                    {
                       table->entries[table->fifo_tail].next_in_fifo = entry_id;
                       table->fifo_tail = entry_id;
                    }
                  PROTECT(table);
                  if (table->free_entries == MAX_ENTRY_ID)
                    {
                       UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
                       TABLE_FROM_IDS = NULL;
                       PROTECT(tdata->eo_ids_tables[mid_table_id]);
                       // Recycle or free the empty table
                       if (!tdata->empty_table) tdata->empty_table = table;
                       else _eo_id_mem_free(table);
                       if (tdata->current_table == table)
                         tdata->current_table = NULL;
                    }
                  // In case an object is destroyed, wipe out the cache
                  if (tdata->cache.id == obj_id)
                    {
                       tdata->cache.id = 0;
                       tdata->cache.object = NULL;
                    }
                  if ((Eo_Id)tdata->cache.isa_id == obj_id)
                    {
                       tdata->cache.isa_id = NULL;
                       tdata->cache.klass = NULL;;
                       tdata->cache.isa = EINA_FALSE;
                    }
                  eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
                  return;
               }
          }
        eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
     }
   ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.", (void *)obj_id);
}

static inline void
_eo_free_ids_tables(Eo_Id_Data *data)
{
   Eo_Id_Table_Data *tdata;

   if (!data) return;
   tdata = data->tables[data->local_domain];
   for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
     {
        if (tdata->eo_ids_tables[mid_table_id])
          {
             for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
               {
                  if (TABLE_FROM_IDS)
                    {
                       _eo_id_mem_free(TABLE_FROM_IDS);
                    }
               }
             _eo_id_mem_free(tdata->eo_ids_tables[mid_table_id]);
          }
        tdata->eo_ids_tables[mid_table_id] = NULL;
     }
   if (tdata->empty_table) _eo_id_mem_free(tdata->empty_table);
   tdata->empty_table = tdata->current_table = NULL;
   _eo_table_data_table_free(tdata);
   data->tables[data->local_domain] = NULL;
   free(data);
}

#ifdef EFL_DEBUG
static inline void
_eo_print(Eo_Id_Table_Data *tdata)
{
   _Eo_Id_Entry *entry;
   unsigned long obj_number = 0;

   for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
     {
        if (tdata->eo_ids_tables[mid_table_id])
          {
             for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
               {
                  if (TABLE_FROM_IDS)
                    {
                       for (Table_Index entry_id = 0; entry_id < MAX_ENTRY_ID; entry_id++)
                         {
                            entry = &(TABLE_FROM_IDS->entries[entry_id]);
                            if (entry->active)
                              {
                                 printf("%ld: %p -> (%p, %p, %p, %p)\n", obj_number++,
                                       entry->ptr,
                                       (void *)mid_table_id, (void *)table_id, (void *)entry_id,
                                       (void *)entry->generation);
                              }
                         }
                    }
               }
          }
     }
}
#endif