Eo: pointers indirection mechanism for objects and classes

Summary: This feature replaces Eo pointers with ids to prevent bad usage
or reuse of these pointers. It doesn't change API.
The mechanism uses tables storing the real pointers to the objects.
See the src/lib/eo/eo_ptr_indirection.c file for more details on the
mechanism.
This commit is contained in:
Daniel Zaoui 2013-04-18 14:19:02 +03:00
parent 33a46372c9
commit 337fac0e73
7 changed files with 605 additions and 183 deletions

View File

@ -338,6 +338,8 @@ AC_SUBST([EINA_SIZEOF_WCHAR_T])
AC_CHECK_SIZEOF(int, 4)
AC_CHECK_SIZEOF(long, 4)
AC_CHECK_SIZEOF([uintptr_t])
AC_CHECK_TYPES([siginfo_t], [], [],
[[
#include <signal.h>
@ -1036,6 +1038,19 @@ AC_ARG_ENABLE([fribidi],
],
[want_fribidi="yes"])
# Eo Id
AC_ARG_ENABLE([eo_id],
[AC_HELP_STRING([--disable-eo-id],
[disable Eo indirection. @<:@default=enabled@:>@])],
[
if test "x${enableval}" = "xyes" ; then
want_eo_id="yes"
else
want_eo_id="no"
fi
],
[want_eo_id="yes"])
# Harfbuzz
AC_ARG_ENABLE([harfbuzz],
[AC_HELP_STRING([--enable-harfbuzz],
@ -3103,6 +3118,10 @@ dnl TODO: remove these ifdefs from code!
AC_DEFINE([HAVE_EIO], [1], [Have eio library])
#### End of Eio
# Eo Id
if test "x${want_eo_id}" = "xyes" ; then
AC_DEFINE([HAVE_EO_ID], [1], [Have eo id])
fi
#### EDBus
EFL_LIB_START([EDBus])
@ -3723,6 +3742,8 @@ AC_OUTPUT
#### Info
EFL_ADD_FEATURE([EO], [eo-id], [${want_eo_id}])
case $host_cpu in
i*86|x86_64|amd64)
EFL_ADD_FEATURE([cpu], [mmx], [${build_cpu_mmx}])
@ -3775,6 +3796,7 @@ echo " Image Loaders.: ${features_evas_loader}"
if test "x${have_pixman}" = "xyes" ; then
echo " Pixman........: ${features_evas_pixman}"
fi
echo "Eo..............: yes (${features_eo})"
echo "Eina............: yes (${features_eina})"
echo "Ecore...........: yes (${features_ecore})"
echo "Ecore_Con.......: yes (${features_ecore_con})"

View File

@ -8,6 +8,7 @@ dist_installed_eomainheaders_DATA = lib/eo/Eo.h
lib_eo_libeo_la_SOURCES = \
lib/eo/eo.c \
lib/eo/eo_ptr_indirection.c \
lib/eo/eo_base_class.c \
lib/eo/eo_private.h

View File

@ -218,7 +218,8 @@ EAPI void eo_dbg_info_free(Eo_Dbg_Info *info);
* @typedef Eo
* The basic Object type.
*/
typedef struct _Eo Eo;
typedef struct _Eo_Opaque Eo;
/**
* @typedef Eo_Op
* The Eo operation type id.
@ -230,14 +231,7 @@ typedef unsigned int Eo_Op;
* The basic Object class type.
* @ingroup Eo_Class
*/
typedef struct _Eo_Class Eo_Class;
/**
* @typedef Eo_Class_Id
* An Id of a class.
* @ingroup Eo_Class
*/
typedef size_t Eo_Class_Id;
typedef struct _Eo_Class_Opaque Eo_Class;
/**
* @def EO_NOOP

File diff suppressed because it is too large Load Diff

View File

@ -56,4 +56,6 @@ extern int _eo_log_dom;
void _eo_condtor_done(Eo *obj);
typedef struct _Eo_Internal _Eo;
#endif

View File

@ -0,0 +1,282 @@
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "eo_ptr_indirection.h"
/* 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 an index to an entry into a ids table.
* For a better memory usage, we don't allocate all the tables at the beginning,
* but only when needed (i.e no more empty entries in allocated tables.
* In addition, tables are composed of intermediate tables, this for memory
* optimizations. Finding the different table, intermediate table and relative
* entry is done by bits manipulation of the id:
*
* id = Table | Inter_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:
* - pointers to the objects
* - generations assigned to the objects
* - a boolean table indicating if an entry is active
* - an index 'start' indicating which entry is the next one to use.
* - a queue that will help us to store the unused entries. 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.
* When an entry is searched into a table, we first try to pop from the
* queue. If a NULL value is returned, we have to use one of the entries that
* have never been used. If a such entry doesn't exist, we pass to the next
* table. Otherwise, we reserve this entry to the object pointer and create
* the id with the table id, the intermediate table id, the entry and a
* generation.
* When an object is freed, the entry into the table is released by pushing
* it into the queue.
*/
#if SIZEOF_UINTPTR_T == 4
/* 32 bits */
# define BITS_FOR_IDS_TABLE 8
# define BITS_FOR_IDS_INTER_TABLE 4
# define BITS_FOR_ID_IN_TABLE 10
# define BITS_FOR_GENERATION_COUNTER 10
#else
/* 64 bits */
# define BITS_FOR_IDS_TABLE 8
# define BITS_FOR_IDS_INTER_TABLE 20
# define BITS_FOR_ID_IN_TABLE 16
# define BITS_FOR_GENERATION_COUNTER 20
#endif
typedef uintptr_t Table_Index;
/* Shifts macros to manipulate the Eo id */
#define SHIFT_FOR_IDS_TABLE \
(BITS_FOR_IDS_INTER_TABLE + BITS_FOR_ID_IN_TABLE + BITS_FOR_GENERATION_COUNTER)
#define SHIFT_FOR_IDS_INTER_TABLE \
(BITS_FOR_ID_IN_TABLE + BITS_FOR_GENERATION_COUNTER)
#define SHIFT_FOR_ID_IN_TABLE (BITS_FOR_GENERATION_COUNTER)
/* Maximum ranges */
#define MAX_IDS_TABLES (1 << BITS_FOR_IDS_TABLE)
#define MAX_IDS_INTER_TABLES (1 << BITS_FOR_IDS_INTER_TABLE)
#define MAX_IDS_PER_TABLE (1 << BITS_FOR_ID_IN_TABLE)
#define MAX_GENERATIONS (1 << BITS_FOR_GENERATION_COUNTER)
/* Table */
typedef struct
{
/* Pointers of objects stored in table */
_Eo *ptrs[MAX_IDS_PER_TABLE];
/* Generations */
Table_Index generation[MAX_IDS_PER_TABLE];
/* Active flags */
char active[MAX_IDS_PER_TABLE >> 3];
/* Queue to handle free entries */
Eina_Trash *queue;
/* Indicates where start the "never used" entries */
Table_Index start;
} _Eo_Ids_Table;
/* Tables handling pointers indirection */
_Eo_Ids_Table **_eo_ids_tables[MAX_IDS_TABLES] = { NULL };
/* Next generation to use when assigning a new entry to a Eo pointer */
Table_Index _eo_generation_counter;
/* Internal macro for active flag manipulation */
#define _ENTRY_ACTIVE_DO_OP(table, id_in_table, op) \
(table)->active[(id_in_table) >> 3] op (1 << ((id_in_table) % 8))
/* Macro that indicates if an entry is active */
#define IS_ENTRY_ACTIVE(table, id_in_table) \
(_ENTRY_ACTIVE_DO_OP(table, id_in_table, &))
/* Macro that activates an entry */
#define ACTIVATE_ENTRY(table, id_in_table) \
_ENTRY_ACTIVE_DO_OP(table, id_in_table, |=)
/* Macro that de-activates an entry */
#define DEACTIVATE_ENTRY(table, id_in_table) \
_ENTRY_ACTIVE_DO_OP(table, id_in_table, &=~)
/* Macro used to compose an Eo id */
#define EO_COMPOSE_ID(TABLE, INTER_TABLE, ENTRY, GENERATION) \
(Eo_Id)(((TABLE & (MAX_IDS_TABLES - 1)) << SHIFT_FOR_IDS_TABLE) | \
((INTER_TABLE & (MAX_IDS_INTER_TABLES - 1)) << SHIFT_FOR_IDS_INTER_TABLE) |\
((ENTRY & (MAX_IDS_PER_TABLE - 1)) << SHIFT_FOR_ID_IN_TABLE) | \
(GENERATION & (MAX_GENERATIONS - 1) ))
/* Macro to extract from an Eo id the indexes of the tables */
#define EO_DECOMPOSE_ID(ID, TABLE, INTER_TABLE, ENTRY, GENERATION) \
TABLE = (ID >> SHIFT_FOR_IDS_TABLE) & (MAX_IDS_TABLES - 1); \
INTER_TABLE = (ID >> SHIFT_FOR_IDS_INTER_TABLE) & (MAX_IDS_INTER_TABLES - 1); \
ENTRY = (ID >> SHIFT_FOR_ID_IN_TABLE) & (MAX_IDS_PER_TABLE - 1); \
GENERATION = ID & (MAX_GENERATIONS - 1); \
/* Macro used for readability */
#define ID_TABLE _eo_ids_tables[table_id][int_table_id]
_Eo *
_eo_obj_pointer_get(const Eo_Id obj_id)
{
#ifdef HAVE_EO_ID
Table_Index table_id, int_table_id, entry_id, generation;
EO_DECOMPOSE_ID((Table_Index) obj_id, table_id, int_table_id, entry_id, generation);
/* Checking the validity of the entry */
if (_eo_ids_tables[table_id] && ID_TABLE && IS_ENTRY_ACTIVE(ID_TABLE, entry_id) &&
ID_TABLE->generation[entry_id] == generation)
return ID_TABLE->ptrs[entry_id];
ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.",
(void *)obj_id);
return NULL;
#else
return (_Eo *)obj_id;
#endif
}
Eo_Id
_eo_id_allocate(const _Eo *obj)
{
#ifdef HAVE_EO_ID
Eo_Id ret = 0;
for (Table_Index table_id = 1; table_id < MAX_IDS_TABLES; table_id++)
{
if (!_eo_ids_tables[table_id])
{
/* We allocate a new table */
_eo_ids_tables[table_id] = calloc(MAX_IDS_INTER_TABLES, sizeof(_Eo_Ids_Table*));
}
for (Table_Index int_table_id = 0; int_table_id < MAX_IDS_INTER_TABLES; int_table_id++)
{
_Eo **ptr;
if (!ID_TABLE)
{
/* We allocate a new intermediate table */
ID_TABLE = calloc(1, sizeof(_Eo_Ids_Table));
eina_trash_init(&(ID_TABLE->queue));
/* We select directly the first entry of the new table */
ptr = &(ID_TABLE->ptrs[0]);
ID_TABLE->start = 1;
}
else
{
/* We try to pop from the queue an unused entry */
ptr = (_Eo **)eina_trash_pop(&(ID_TABLE->queue));
}
if (!ptr && ID_TABLE->start < MAX_IDS_PER_TABLE)
{
/* No more unused entries in the trash but still empty entries in the table */
ptr = &(ID_TABLE->ptrs[ID_TABLE->start]);
ID_TABLE->start++;
}
if (ptr)
{
/* An entry was found - need to find the entry id and fill it */
Table_Index id = ptr - ID_TABLE->ptrs;
ID_TABLE->generation[id] = _eo_generation_counter;
ACTIVATE_ENTRY(ID_TABLE, id);
*ptr = (_Eo *)obj;
ret = EO_COMPOSE_ID(table_id, int_table_id, id, _eo_generation_counter);
_eo_generation_counter++;
_eo_generation_counter %= MAX_GENERATIONS;
return ret;
}
}
}
return ret;
#else
return (Eo_Id)obj;
#endif
}
void
_eo_id_release(const Eo_Id obj_id)
{
#ifdef HAVE_EO_ID
Table_Index table_id, int_table_id, entry_id, generation;
EO_DECOMPOSE_ID((Table_Index) obj_id, table_id, int_table_id, entry_id, generation);
/* Checking the validity of the entry */
if (!_eo_ids_tables[table_id]) goto error;
if (!ID_TABLE) goto error;
if (ID_TABLE->generation[entry_id] != generation) goto error;
/* Disable the entry */
DEACTIVATE_ENTRY(ID_TABLE, entry_id);
/* Push the entry into the queue */
eina_trash_push(&(ID_TABLE->queue), &(ID_TABLE->ptrs[entry_id]));
return;
error:
ERR("obj_id %p is not pointing to a valid object. Maybe it has already been freed.", (void *)obj_id);
#else
(void) obj_id;
#endif
}
void
_eo_free_ids_tables()
{
for (Table_Index table_id = 0; table_id < MAX_IDS_TABLES; table_id++)
{
if (_eo_ids_tables[table_id])
{
for (Table_Index int_table_id = 0; int_table_id < MAX_IDS_INTER_TABLES; int_table_id++)
{
if (ID_TABLE)
{
free(ID_TABLE);
}
}
free(_eo_ids_tables[table_id]);
}
_eo_ids_tables[table_id] = NULL;
}
}
#ifdef EFL_DEBUG
void
_eo_print()
{
unsigned long obj_number = 0;
for (Table_Index table_id = 0; table_id < MAX_IDS_TABLES; table_id++)
{
if (_eo_ids_tables[table_id])
{
for (Table_Index int_table_id = 0; int_table_id < MAX_IDS_INTER_TABLES; int_table_id++)
{
if (ID_TABLE)
{
for (Table_Index entry_id = 0; entry_id < MAX_IDS_PER_TABLE; entry_id++)
{
if (IS_ENTRY_ACTIVE(ID_TABLE, entry_id))
{
printf("%ld: %p -> (%p, %p, %p, %p)\n", obj_number++,
ID_TABLE->ptrs[entry_id],
(void *)table_id, (void *)int_table_id, (void *)entry_id,
(void *)ID_TABLE->generation[entry_id]);
}
}
}
}
}
}
}
#endif

View File

@ -0,0 +1,62 @@
#ifndef EO_PTR_INDIRECTION_H
#define EO_PTR_INDIRECTION_H
#include "Eo.h"
#include "eo_private.h"
typedef size_t Eo_Id;
/* Retrieves the pointer to the object from the id */
_Eo *_eo_obj_pointer_get(const Eo_Id obj_id);
/* Allocates an entry for the given object */
Eo_Id _eo_id_allocate(const _Eo *obj);
/* Releases an entry by the object id */
void _eo_id_release(const Eo_Id obj_id);
/* Free all the entries and the tables */
void _eo_free_ids_tables();
/* Macro used to obtain the object pointer and return if fails. */
#ifdef HAVE_EO_ID
#define EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, ret) \
_Eo *obj; \
do { \
obj = _eo_obj_pointer_get((Eo_Id)obj_id); \
if (!obj) return ret; \
} while (0)
#define EO_OBJ_POINTER_RETURN(obj_id, obj) \
_Eo *obj; \
do { \
obj = _eo_obj_pointer_get((Eo_Id)obj_id); \
if (!obj) return; \
} while (0)
#else
#define EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, ret) \
_Eo *obj; \
do { \
obj = _eo_obj_pointer_get(obj_id); \
EO_MAGIC_RETURN_VAL(obj, EO_EINA_MAGIC, ret); \
} while (0)
#define EO_OBJ_POINTER_RETURN(obj_id, obj) \
_Eo *obj; \
do { \
obj = _eo_obj_pointer_get(obj_id); \
EO_MAGIC_RETURN(obj, EO_EINA_MAGIC); \
} while (0)
#endif
#ifdef EFL_DEBUG
void _eo_print();
#endif
#endif