10 # include <valgrind.h>
11 # include <memcheck.h>
14 /* Start of pointer indirection:
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
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.
27 * An Eo id is contructed by bits manipulation of table indexes and a generation.
29 * id = Mid Table | Table | Entry | Generation
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.
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
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.
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.
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
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
71 #if SIZEOF_UINTPTR_T == 4
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
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;
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
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;
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;
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)
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)
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))
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))
143 #define MEM_HEADER_SIZE 16
144 #define MEM_PAGE_SIZE 4096
145 #define MEM_MAGIC 0x3f61ec8a
147 typedef struct _Mem_Header
154 _eo_id_mem_alloc(size_t size)
157 # ifdef HAVE_VALGRIND
158 if (RUNNING_ON_VALGRIND) return malloc(size);
165 newsize = MEM_PAGE_SIZE * ((size + MEM_HEADER_SIZE + MEM_PAGE_SIZE - 1) /
167 ptr = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
168 MAP_PRIVATE | MAP_ANON, -1, 0);
169 if (ptr == MAP_FAILED)
171 ERR("mmap of eo id table region failed!");
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);
186 _eo_id_mem_calloc(size_t num, size_t size)
188 void *ptr = _eo_id_mem_alloc(num * size);
189 if (!ptr) return NULL;
190 memset(ptr, 0, num * size);
195 _eo_id_mem_free(void *ptr)
198 # ifdef HAVE_VALGRIND
199 if (RUNNING_ON_VALGRIND) free(ptr);
205 hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
206 if (hdr->magic != MEM_MAGIC)
208 ERR("unmap of eo table region has bad magic!");
211 munmap(hdr, hdr->size);
218 #ifdef EINA_DEBUG_MALLOC
220 _eo_id_mem_protect(void *ptr, Eina_Bool may_not_write)
223 # ifdef HAVE_VALGRIND
224 if (RUNNING_ON_VALGRIND) { return; }
230 hdr = (Mem_Header *)(((unsigned char *)ptr) - MEM_HEADER_SIZE);
231 if (hdr->magic != MEM_MAGIC)
233 ERR("mprotect of eo table region has bad magic!");
236 mprotect(hdr, hdr->size, PROT_READ | ( may_not_write ? 0 : PROT_WRITE) );
240 # define PROTECT(_ptr_) _eo_id_mem_protect((_ptr_), EINA_TRUE)
241 # define UNPROTECT(_ptr_) _eo_id_mem_protect((_ptr_), EINA_FALSE)
243 # define PROTECT(_ptr_)
244 # define UNPROTECT(_ptr_)
247 #define EO_ALIGN_SIZE(size) eina_mempool_alignof(size)
252 /* Pointer to the object */
254 /* Indicates where to find the next entry to recycle */
255 Table_Index next_in_fifo;
257 unsigned int active : 1;
259 unsigned int generation : BITS_GENERATION_COUNTER;
266 /* Indicates where start the "never used" entries */
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 */
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];
280 //////////////////////////////////////////////////////////////////////////
282 typedef struct _Eo_Id_Data Eo_Id_Data;
283 typedef struct _Eo_Id_Table_Data Eo_Id_Table_Data;
285 struct _Eo_Id_Table_Data
287 /* Cached eoid lookups */
293 const Efl_Class *klass;
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 */
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? */
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];
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;
323 /* TIZEN_ONLY(20180518): See _eo_table_data_get() */
324 #ifdef DISABLE_EO_THREAD_CHECK
325 extern Eo_Id_Data *_tizen_eo_table_data;
328 static inline Eo_Id_Table_Data *
329 _eo_table_data_table_new(Efl_Id_Domain domain)
331 Eo_Id_Table_Data *tdata;
333 tdata = calloc(1, sizeof(Eo_Id_Table_Data));
334 if (!tdata) return NULL;
335 if (domain == EFL_ID_DOMAIN_SHARED)
337 if (!eina_lock_recursive_new(&(tdata->obj_lock)))
342 tdata->shared = EINA_TRUE;
344 tdata->generation = rand() % MAX_GENERATIONS;
348 static inline Eo_Id_Data *
349 _eo_table_data_new(Efl_Id_Domain domain)
353 data = calloc(1, sizeof(Eo_Id_Data));
354 if (!data) return NULL;
355 data->local_domain = domain;
356 data->domain_stack[data->stack_top] = data->local_domain;
357 data->tables[data->local_domain] =
358 _eo_table_data_table_new(data->local_domain);
359 if (domain != EFL_ID_DOMAIN_SHARED)
360 data->tables[EFL_ID_DOMAIN_SHARED] = _eo_table_data_shared_data;
365 _eo_table_data_table_free(Eo_Id_Table_Data *tdata)
367 if (tdata->shared) eina_lock_free(&(tdata->obj_lock));
371 static inline Eo_Id_Data *
372 _eo_table_data_get(void)
374 /* TIZEN_ONLY(20180518): Turn thread-validation off for unstable Tizen 5.0
375 apps which are violating thread-safety. Eo has gotten more strict-check
376 for thread violiation API calls. Since Tizen 5.0, many apps(which
377 violates EAPI thread safety rules) unexpectedly became unstable cause of
378 thread-validation. We disable this function for now but wish to remove
379 this temporary code in the near future when apps are ready to go. */
380 #ifdef DISABLE_EO_THREAD_CHECK
381 if (_tizen_eo_table_data) return _tizen_eo_table_data;
382 _tizen_eo_table_data = _eo_table_data_new(EFL_ID_DOMAIN_MAIN);
383 return _tizen_eo_table_data;
385 Eo_Id_Data *data = eina_tls_get(_eo_table_data);
386 if (EINA_LIKELY(data != NULL)) return data;
388 data = _eo_table_data_new(EFL_ID_DOMAIN_THREAD);
389 if (!data) return NULL;
391 eina_tls_set(_eo_table_data, data);
396 static inline Eo_Id_Table_Data *
397 _eo_table_data_current_table_get(Eo_Id_Data *data)
399 return data->tables[data->domain_stack[data->stack_top]];
402 static inline Eo_Id_Table_Data *
403 _eo_table_data_table_get(Eo_Id_Data *data, Efl_Id_Domain domain)
405 return data->tables[domain];
408 static inline Eina_Bool
409 _eo_id_domain_compatible(const Eo *o1, const Eo *o2)
411 Efl_Id_Domain domain1 = ((Eo_Id)o1 >> SHIFT_DOMAIN) & MASK_DOMAIN;
412 Efl_Id_Domain domain2 = ((Eo_Id)o2 >> SHIFT_DOMAIN) & MASK_DOMAIN;
413 if (domain1 == domain2) return EINA_TRUE;
414 ERR("Object %p and %p are not compatible. Domain %i and %i do not match",
415 o1, o2, domain1, domain2);
420 _eo_obj_pointer_done(const Eo_Id obj_id)
422 Efl_Id_Domain domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
423 if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return;
424 eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
427 //////////////////////////////////////////////////////////////////////////
430 /* Macro used to compose an Eo id */
431 #define EO_COMPOSE_PARTIAL_ID(MID_TABLE, TABLE) \
432 (((Eo_Id) 0x1 << REF_TAG_SHIFT) | \
433 ((Eo_Id)(MID_TABLE & MASK_MID_TABLE_ID) << SHIFT_MID_TABLE_ID) | \
434 ((Eo_Id)(TABLE & MASK_TABLE_ID) << SHIFT_TABLE_ID))
436 #define EO_COMPOSE_FINAL_ID(PARTIAL_ID, ENTRY, DOMAIN, GENERATION) \
438 (((Eo_Id)DOMAIN & MASK_DOMAIN) << SHIFT_DOMAIN) | \
439 ((ENTRY & MASK_ENTRY_ID) << SHIFT_ENTRY_ID) | \
440 (GENERATION & MASK_GENERATIONS))
442 /* Macro to extract from an Eo id the indexes of the tables */
443 #define EO_DECOMPOSE_ID(ID, MID_TABLE, TABLE, ENTRY, GENERATION) \
444 MID_TABLE = (ID >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID; \
445 TABLE = (ID >> SHIFT_TABLE_ID) & MASK_TABLE_ID; \
446 ENTRY = (ID >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID; \
447 GENERATION = ID & MASK_GENERATIONS;
449 /* Macro used for readability */
450 #define TABLE_FROM_IDS tdata->eo_ids_tables[mid_table_id][table_id]
452 static inline _Eo_Id_Entry *
453 _get_available_entry(_Eo_Ids_Table *table)
455 _Eo_Id_Entry *entry = NULL;
457 if (table->start != MAX_ENTRY_ID)
459 /* Serve never used entries first */
460 entry = &(table->entries[table->start]);
463 table->free_entries--;
465 else if (table->fifo_head != -1)
467 /* Pop a free entry from the fifo */
468 entry = &(table->entries[table->fifo_head]);
470 if (entry->next_in_fifo == -1)
471 table->fifo_head = table->fifo_tail = -1;
473 table->fifo_head = entry->next_in_fifo;
474 table->free_entries--;
480 static inline _Eo_Id_Entry *
481 _search_tables(Eo_Id_Table_Data *tdata)
483 _Eo_Ids_Table *table;
486 if (!tdata) return NULL;
487 for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
489 if (!tdata->eo_ids_tables[mid_table_id])
491 /* Allocate a new intermediate table */
492 tdata->eo_ids_tables[mid_table_id] = _eo_id_mem_calloc(MAX_TABLE_ID, sizeof(_Eo_Ids_Table*));
495 for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
497 table = TABLE_FROM_IDS;
501 if (tdata->empty_table)
503 /* Recycle the available empty table */
504 table = tdata->empty_table;
505 tdata->empty_table = NULL;
510 /* Allocate a new table */
511 table = _eo_id_mem_calloc(1, sizeof(_Eo_Ids_Table));
513 /* Initialize the table and reserve the first entry */
515 table->free_entries = MAX_ENTRY_ID - 1;
516 table->fifo_head = table->fifo_tail = -1;
517 table->partial_id = EO_COMPOSE_PARTIAL_ID(mid_table_id, table_id);
518 entry = &(table->entries[0]);
519 UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
520 TABLE_FROM_IDS = table;
521 PROTECT(tdata->eo_ids_tables[mid_table_id]);
524 entry = _get_available_entry(table);
528 /* Store table info into current table */
529 tdata->current_table = table;
535 ERR("no more available entries to store eo objects");
536 tdata->current_table = NULL;
540 /* Gives a fake id that serves as a marker if eo id is off. */
542 _eo_id_allocate(const _Eo_Object *obj, const Eo *parent_id)
544 _Eo_Id_Entry *entry = NULL;
546 Eo_Id_Table_Data *tdata;
549 data = _eo_table_data_get();
552 Efl_Id_Domain domain = ((Eo_Id)parent_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
553 tdata = _eo_table_data_table_get(data, domain);
555 else tdata = _eo_table_data_current_table_get(data);
556 if (!tdata) return 0;
558 if (EINA_LIKELY(!tdata->shared))
560 if (tdata->current_table)
561 entry = _get_available_entry(tdata->current_table);
563 if (!entry) entry = _search_tables(tdata);
565 if (!tdata->current_table || !entry)
570 UNPROTECT(tdata->current_table);
571 /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
573 if (tdata->generation >= MAX_GENERATIONS) tdata->generation = 1;
574 /* Fill the entry and return it's Eo Id */
575 entry->ptr = (_Eo_Object *)obj;
577 entry->generation = tdata->generation;
578 PROTECT(tdata->current_table);
579 id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
580 (entry - tdata->current_table->entries),
581 data->domain_stack[data->stack_top],
586 eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
587 if (tdata->current_table)
588 entry = _get_available_entry(tdata->current_table);
590 if (!entry) entry = _search_tables(tdata);
592 if (!tdata->current_table || !entry)
598 UNPROTECT(tdata->current_table);
599 /* [1;max-1] thus we never generate an Eo_Id equal to 0 */
601 if (tdata->generation == MAX_GENERATIONS) tdata->generation = 1;
602 /* Fill the entry and return it's Eo Id */
603 entry->ptr = (_Eo_Object *)obj;
605 entry->generation = tdata->generation;
606 PROTECT(tdata->current_table);
607 id = EO_COMPOSE_FINAL_ID(tdata->current_table->partial_id,
608 (entry - tdata->current_table->entries),
609 EFL_ID_DOMAIN_SHARED,
612 eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
618 _eo_id_release(const Eo_Id obj_id)
620 _Eo_Ids_Table *table;
622 Generation_Counter generation;
623 Table_Index mid_table_id, table_id, entry_id;
624 Efl_Id_Domain domain;
626 Eo_Id_Table_Data *tdata;
628 domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
629 data = _eo_table_data_get();
630 tdata = _eo_table_data_table_get(data, domain);
633 EO_DECOMPOSE_ID(obj_id, mid_table_id, table_id, entry_id, generation);
635 if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
637 // Check the validity of the entry
638 if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
640 entry = &(table->entries[entry_id]);
641 if (entry && entry->active && (entry->generation == generation))
644 table->free_entries++;
647 entry->next_in_fifo = -1;
648 // Push the entry into the fifo
649 if (table->fifo_tail == -1)
650 table->fifo_head = table->fifo_tail = entry_id;
653 table->entries[table->fifo_tail].next_in_fifo = entry_id;
654 table->fifo_tail = entry_id;
657 if (table->free_entries == MAX_ENTRY_ID)
659 UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
660 TABLE_FROM_IDS = NULL;
661 PROTECT(tdata->eo_ids_tables[mid_table_id]);
662 // Recycle or free the empty table
663 if (!tdata->empty_table) tdata->empty_table = table;
664 else _eo_id_mem_free(table);
665 if (tdata->current_table == table)
666 tdata->current_table = NULL;
668 // In case an object is destroyed, wipe out the cache
669 if (tdata->cache.id == obj_id)
672 tdata->cache.object = NULL;
674 if ((Eo_Id)tdata->cache.isa_id == obj_id)
676 tdata->cache.isa_id = NULL;
677 tdata->cache.klass = NULL;;
678 tdata->cache.isa = EINA_FALSE;
686 eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
687 // Check the validity of the entry
688 if (tdata->eo_ids_tables[mid_table_id] && (table = TABLE_FROM_IDS))
690 entry = &(table->entries[entry_id]);
691 if (entry && entry->active && (entry->generation == generation))
694 table->free_entries++;
697 entry->next_in_fifo = -1;
698 // Push the entry into the fifo
699 if (table->fifo_tail == -1)
700 table->fifo_head = table->fifo_tail = entry_id;
703 table->entries[table->fifo_tail].next_in_fifo = entry_id;
704 table->fifo_tail = entry_id;
707 if (table->free_entries == MAX_ENTRY_ID)
709 UNPROTECT(tdata->eo_ids_tables[mid_table_id]);
710 TABLE_FROM_IDS = NULL;
711 PROTECT(tdata->eo_ids_tables[mid_table_id]);
712 // Recycle or free the empty table
713 if (!tdata->empty_table) tdata->empty_table = table;
714 else _eo_id_mem_free(table);
715 if (tdata->current_table == table)
716 tdata->current_table = NULL;
718 // In case an object is destroyed, wipe out the cache
719 if (tdata->cache.id == obj_id)
722 tdata->cache.object = NULL;
724 if ((Eo_Id)tdata->cache.isa_id == obj_id)
726 tdata->cache.isa_id = NULL;
727 tdata->cache.klass = NULL;;
728 tdata->cache.isa = EINA_FALSE;
730 eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
734 eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
736 ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.", (void *)obj_id);
740 _eo_free_ids_tables(Eo_Id_Data *data)
742 Eo_Id_Table_Data *tdata;
745 tdata = data->tables[data->local_domain];
746 for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
748 if (tdata->eo_ids_tables[mid_table_id])
750 for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
754 _eo_id_mem_free(TABLE_FROM_IDS);
757 _eo_id_mem_free(tdata->eo_ids_tables[mid_table_id]);
759 tdata->eo_ids_tables[mid_table_id] = NULL;
761 if (tdata->empty_table) _eo_id_mem_free(tdata->empty_table);
762 tdata->empty_table = tdata->current_table = NULL;
763 _eo_table_data_table_free(tdata);
764 data->tables[data->local_domain] = NULL;
770 _eo_print(Eo_Id_Table_Data *tdata)
773 unsigned long obj_number = 0;
775 for (Table_Index mid_table_id = 0; mid_table_id < MAX_MID_TABLE_ID; mid_table_id++)
777 if (tdata->eo_ids_tables[mid_table_id])
779 for (Table_Index table_id = 0; table_id < MAX_TABLE_ID; table_id++)
783 for (Table_Index entry_id = 0; entry_id < MAX_ENTRY_ID; entry_id++)
785 entry = &(TABLE_FROM_IDS->entries[entry_id]);
788 printf("%ld: %p -> (%p, %p, %p, %p)\n", obj_number++,
790 (void *)mid_table_id, (void *)table_id, (void *)entry_id,
791 (void *)entry->generation);