eo: rework vtable allocation scheme

Summary:
with this commit a new way of allocating vtables arrived.
The old mechnism was to allocate a table big enough to carry *all*
functions at once, in order to not allocate that much memory for
functions that are not implemented on a specific klass, dichchains have
been used, which can be seens as a 2D matrix, where columns are only
allocated if min 1 entry needs to be written, this may have been a good
way to allocate back in the day when all this with eo started, however,
it showed to not pay off.

With this new way, we allocate a array of arrays. the first lvl array is
carrying enough slots, that *all* up to the time defined
interfaces/classes/abstracts/mixins can be implemented. The second lvl
array then has exactly the size of the defined APIs. The second lvl
array is obviously only allocated if needed.

When comparing the two methods, i messured two things, the usage based
on memory allocation for vtable-way-1 and vtable-way-2. Additionally, i
checked the overall memory usage of elementary_test using pmap. The
first messurement is a little bit more exact. The second messurement is
more biased, but captures the whole picture.

Memory allocation tracking:
   vtable-way-1 - vtable-way-2 = 74680 Byte

Pmap memory tracking:
   vtable-way1 - vtable-way-2 = 217088 Byte

The second messurement shows a bigger impact, likely because this is
also showing off all the sideeffects that we are taking place due to
fewer allocations.

Depends on D11524

Reviewers: zmike, tasn, stefan_schmidt, woohyun, cedric, raster

Subscribers: #reviewers, #committers

Tags: #efl

Differential Revision: https://phab.enlightenment.org/D11535
This commit is contained in:
Marcel Hollerbach 2020-03-20 11:32:38 +00:00 committed by Carsten Haitzler (Rasterman)
parent 64f7edc7fc
commit 3bd16a46f1
3 changed files with 297 additions and 255 deletions

View File

@ -90,7 +90,6 @@ static _Efl_Class **_eo_classes = NULL;
static Eo_Id _eo_classes_last_id = 0;
static Eo_Id _eo_classes_alloc = 0;
static int _efl_object_init_count = 0;
static Efl_Object_Op _eo_ops_last_id = 0;
static Eina_Hash *_ops_storage = NULL;
static Eina_Spinlock _ops_storage_lock;
@ -104,7 +103,6 @@ static void _eo_condtor_reset(_Eo_Object *obj);
static inline void *_efl_data_scope_get(const _Eo_Object *obj, const _Efl_Class *klass);
static inline void *_efl_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj);
static inline void _efl_data_xunref_internal(_Eo_Object *obj, void *data, const _Eo_Object *ref_obj);
static void _vtable_init(Eo_Vtable *vtable, size_t size);
static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_func);
@ -120,96 +118,175 @@ static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_f
(_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \
})
static inline void
_vtable_chain2_unref(Dich_Chain2 *chain)
#define EFL_OBJECT_OP_CLASS_PART(op) op >> 16
#define EFL_OBJECT_OP_FUNC_PART(op) op & 0xffff
#define EFL_OBJECT_OP_CREATE_OP_ID(class_id, func_id) ((unsigned short)class_id)<<16|((unsigned short)func_id&0xffff)
static const _Efl_Class *
_eo_op_class_get(Efl_Object_Op op)
{
if (--(chain->refcount) == 0)
{
free(chain);
}
short class_id = EFL_OBJECT_OP_CLASS_PART(op);
return _eo_classes[class_id];
}
static inline void
_vtable_chain_alloc(Dich_Chain1 *chain1)
/**
* This inits the vtable wit hthe current size of allocated tables
*/
static void
_vtable_init(Eo_Vtable *vtable)
{
chain1->chain2 = calloc(1, sizeof(*(chain1->chain2)));
chain1->chain2->refcount = 1;
//we assume here that _eo_classes_last_id was called before
vtable->size = _eo_classes_last_id;
vtable->chain = calloc(vtable->size, sizeof(Eo_Vtable_Node));
}
static inline void _vtable_chain_write_prepare(Dich_Chain1 *dst);
static inline void
_vtable_chain_merge(Dich_Chain1 *dst, const Dich_Chain1 *src)
/**
* This removes all nodes from the klass that are copied from mro
*/
static void
_vtable_mro_free(const _Efl_Class *klass)
{
size_t j;
const op_type_funcs *sf = src->chain2->funcs;
op_type_funcs *df = dst->chain2->funcs;
const _Efl_Class **mro_itr = klass->mro;
const Eo_Vtable *vtable = &klass->vtable;
if (df == sf)
for ( ; *mro_itr ; mro_itr++)
{
/* Skip if the chain is the same. */
return;
}
for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++)
{
if (sf->func && memcmp(df, sf, sizeof(*df)))
const Eo_Vtable *mro_vtable = &(*mro_itr)->vtable;
for (int i = 0; i < mro_vtable->size; ++i)
{
_vtable_chain_write_prepare(dst);
df = dst->chain2->funcs + j;
memcpy(df, sf, sizeof(*df));
if (mro_vtable->chain[i].funcs == vtable->chain[i].funcs)
vtable->chain[i].funcs = NULL;
}
}
}
static inline void
_vtable_chain_write_prepare(Dich_Chain1 *dst)
static void
_vtable_free(Eo_Vtable *vtable, const Eo_Vtable *root)
{
if (!dst->chain2)
if (root)
{
_vtable_chain_alloc(dst);
return;
}
else if (dst->chain2->refcount == 1)
{
/* We own it, no need to duplicate */
return;
EINA_SAFETY_ON_FALSE_RETURN(vtable->size == root->size);
}
Dich_Chain1 old;
old.chain2 = dst->chain2;
_vtable_chain_alloc(dst);
_vtable_chain_merge(dst, &old);
_vtable_chain2_unref(old.chain2);
}
static inline void
_vtable_chain_copy_ref(Dich_Chain1 *dst, const Dich_Chain1 *src)
{
if (dst->chain2)
for (int i = 0; i < vtable->size; ++i)
{
_vtable_chain_merge(dst, src);
}
else
{
dst->chain2 = src->chain2;
dst->chain2->refcount++;
}
}
if (root && root->chain[i].funcs == vtable->chain[i].funcs)
vtable->chain[i].count = 0;
static inline void
_vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
{
Efl_Object_Op i;
const Dich_Chain1 *sc1 = src->chain;
Dich_Chain1 *dc1 = dst->chain;
for (i = 0 ; i < src->size ; i++, sc1++, dc1++)
{
if (sc1->chain2)
if (vtable->chain[i].count)
{
_vtable_chain_copy_ref(dc1, sc1);
free(vtable->chain[i].funcs);
}
}
free(vtable->chain);
}
/**
* This takes over all set chains of the src to dest.
* This should only be called on Eo_Vtables, which are initialized with this value.
* Previous setted values are going to be overwritten.
*/
static void
_vtable_take_over(Eo_Vtable *dest, const Eo_Vtable *src)
{
for (int i = 0; i < src->size; ++i)
{
if (src->chain[i].funcs)
{
dest->chain[i] = src->chain[i];
}
}
}
/**
* Fills the node of the passed class id with a empty none NULL pointer.
* This is used to indicate that a specific node has a normal 0 size, but is set.
*/
static void
_vtable_insert_empty_funcs(Eo_Vtable *vtable, unsigned short class_id)
{
vtable->chain[class_id].funcs = (void*)0x1010101;
vtable->chain[class_id].count = 0;
}
/**
* duplicate the source node, and write the duplicated values to the destination
* No logical changes are applied to src.
*/
static void
_vtable_copy_node(Eo_Vtable_Node *dest, const Eo_Vtable_Node *src)
{
dest->count = src->count;
dest->funcs = calloc(sizeof(op_type_funcs), src->count);
memcpy(dest->funcs, src->funcs, sizeof(op_type_funcs) * src->count);
}
/**
* Initialize a node with a empty funcs array of the passed length
*/
static void
_vtable_prepare_empty_node(Eo_Vtable *dest, unsigned int length, unsigned int class_id)
{
dest->chain[class_id].count = length;
dest->chain[class_id].funcs = calloc(sizeof(op_type_funcs), dest->chain[class_id].count);
}
/**
* Copy all setted APIs from src to dest.
* Already set function slots are going to be replaced.
*/
static void
_vtable_merge_defined_api(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
{
for (unsigned int i = 0; i < src->size; ++i)
{
//if there is a source node evalulate if we need to copy it
if (src->chain[i].funcs)
{
if (!dest->chain[i].funcs)
{
dest->chain[i] = src->chain[i];
EINA_SAFETY_ON_FALSE_RETURN(hitmap[i] == EINA_FALSE);
}
else
{
if (!hitmap[i])
{
const Eo_Vtable_Node node = dest->chain[i];
_vtable_copy_node(&dest->chain[i], &node); //we copy what we have, and overwrite in the later for loop
hitmap[i] = EINA_TRUE;
}
for (int j = 0; j < src->chain[i].count; ++j)
{
if (src->chain[i].funcs[j].func)
dest->chain[i].funcs[j] = src->chain[i].funcs[j];
}
}
}
}
}
/**
* Ensure that all set nodes from src are also set on dest.
* No real values are copied, the newly taken or allocated slots will be empty.
*/
static void
_vtable_merge_empty(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
{
for (unsigned int i = 0; i < src->size; ++i)
{
if (src->chain[i].funcs && !dest->chain[i].funcs)
{
if (!src->chain[i].count)
{
dest->chain[i].funcs = src->chain[i].funcs;
dest->chain[i].count = src->chain[i].count;
}
else
{
_vtable_prepare_empty_node(dest, src->chain[i].count, i);
hitmap[i] = EINA_TRUE;
}
}
}
}
@ -217,37 +294,15 @@ _vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
static inline const op_type_funcs *
_vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op op)
{
size_t idx1 = DICH_CHAIN1(op);
if (EINA_UNLIKELY(idx1 >= vtable->size))
return NULL;
Dich_Chain1 *chain1 = &vtable->chain[idx1];
if (EINA_UNLIKELY(!chain1->chain2))
return NULL;
return &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
}
unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
/* XXX: Only used for a debug message below. Doesn't matter that it's slow. */
static const _Efl_Class *
_eo_op_class_get(Efl_Object_Op op)
{
_Efl_Class **itr = _eo_classes;
int mid, max, min;
if (EINA_UNLIKELY(vtable->size <= class_id))
return NULL;
if (EINA_UNLIKELY(vtable->chain[class_id].count <= func_id))
return NULL;
min = 0;
max = _eo_classes_last_id - 1;
while (min <= max)
{
mid = (min + max) / 2;
if (itr[mid]->base_id + itr[mid]->ops_count < op)
min = mid + 1;
else if (itr[mid]->base_id > op)
max = mid - 1;
else
return itr[mid];
}
return NULL;
return &vtable->chain[class_id].funcs[func_id];
}
static inline Eina_Bool
@ -256,24 +311,30 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
Eo_Op_Func_Type func, Eina_Bool allow_same_override)
{
op_type_funcs *fsrc;
size_t idx1 = DICH_CHAIN1(op);
Dich_Chain1 *chain1;
unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
Eo_Vtable_Node *hirachy_node = NULL;
Eo_Vtable_Node *node = NULL;
EINA_SAFETY_ON_FALSE_RETURN_VAL(idx1 < vtable->size, EINA_FALSE);
chain1 = &vtable->chain[idx1];
_vtable_chain_write_prepare(chain1);
fsrc = &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
EINA_SAFETY_ON_FALSE_RETURN_VAL(vtable->size >= class_id, EINA_FALSE);
if (klass->parent && klass->parent->vtable.size > class_id)
hirachy_node = &klass->parent->vtable.chain[class_id];
if (hierarchy_klass)
hirachy_node = &hierarchy_klass->vtable.chain[class_id];
node = &vtable->chain[class_id];
EINA_SAFETY_ON_NULL_RETURN_VAL(node->funcs, EINA_FALSE);
EINA_SAFETY_ON_FALSE_RETURN_VAL(node->count >= func_id, EINA_FALSE);
fsrc = &node->funcs[func_id];
if (hierarchy_klass && !func)
{
if (!func)
{
op_type_funcs *fsrc_orig;
Dich_Chain1 *chain1_orig;
chain1_orig = &hierarchy_klass->vtable.chain[idx1];
fsrc_orig = &chain1_orig->chain2->funcs[DICH_CHAIN_LAST(op)];
func = fsrc_orig->func;
klass = fsrc_orig->src;
op_type_funcs funcs = hirachy_node->funcs[func_id];
klass = funcs.src;
func = funcs.func;
}
}
else
@ -287,27 +348,12 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
}
}
fsrc->func = func;
fsrc->src = klass;
fsrc->func = func;
return EINA_TRUE;
}
void
_vtable_func_clean_all(Eo_Vtable *vtable)
{
size_t i;
Dich_Chain1 *chain1 = vtable->chain;
for (i = 0 ; i < vtable->size ; i++, chain1++)
{
if (chain1->chain2)
_vtable_chain2_unref(chain1->chain2);
}
free(vtable->chain);
vtable->chain = NULL;
}
/* END OF DICH */
#define _EO_ID_GET(Id) ((Eo_Id) (Id))
@ -478,7 +524,7 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
obj = _obj;
klass = _obj->klass;
vtable = EO_VTABLE(obj);
vtable = EO_VTABLE2(obj);
if (EINA_UNLIKELY(_obj->cur_klass != NULL))
{
// YES this is a goto with a label to return. this is a
@ -544,7 +590,7 @@ end:
EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
if (EINA_UNLIKELY(!emb_obj)) continue;
func = _vtable_func_get(EO_VTABLE(emb_obj), op);
func = _vtable_func_get(&emb_obj->klass->vtable, op);
if (func == NULL) goto composite_continue;
if (EINA_LIKELY(func->func && func->src))
@ -727,23 +773,25 @@ _efl_object_op_api_id_get(const void *api_func, const Eo *eo_obj, const char *ap
/* klass is the klass we are working on. hierarchy_klass is the class whe should
* use when validating. */
static Eina_Bool
_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Class *hierarchy_klass, const _Efl_Class *klass, Efl_Object_Op id_offset, Eina_Bool override_only)
_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Class *hierarchy_klass, const _Efl_Class *klass, Eina_Bool override_only, unsigned int class_id, Eina_Bool *hitmap)
{
unsigned int i, j;
Efl_Object_Op op_id;
unsigned int number_of_new_functions = 0;
const Efl_Op_Description *op_desc;
const Efl_Op_Description *op_descs;
const _Efl_Class *override_class;
const void **api_funcs;
Eina_Bool check_equal;
op_id = hierarchy_klass->base_id + id_offset;
op_descs = ops->descs;
override_class = override_only ? hierarchy_klass : NULL;
DBG("Set functions for class '%s':%p", klass->desc->name, klass);
if (!op_descs || !ops->count) return EINA_TRUE;
if (!override_only)
_vtable_insert_empty_funcs(vtable, class_id);
if (!op_descs || !ops->count)
return EINA_TRUE;
#ifdef EO_DEBUG
check_equal = EINA_TRUE;
@ -776,43 +824,61 @@ _eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Cla
api_funcs[i] = op_desc->api_func;
}
if (_efl_object_api_op_id_get_internal(op_desc->api_func) == EFL_NOOP)
{
number_of_new_functions ++;
}
}
for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
if (!override_only)
{
Efl_Object_Op op = EFL_NOOP;
//Before setting any real functions, allocate the node that will contain all the functions
_vtable_prepare_empty_node(vtable, number_of_new_functions, class_id);
hitmap[class_id] = EINA_TRUE;
}
for (i = 0, j = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
{
Efl_Object_Op op2 = EFL_NOOP;
short op2_class_id;
/* Get the opid for the function. */
op = _efl_object_api_op_id_get_internal(op_desc->api_func);
op2 = _efl_object_api_op_id_get_internal(op_desc->api_func);
if (op == EFL_NOOP)
if (op2 == EFL_NOOP)
{
//functions that do not have a op yet, are considered to be belonging to this class
if (override_only)
{
ERR("Class '%s': Tried overriding a previously undefined function.", klass->desc->name);
return EINA_FALSE;
}
op = op_id;
op2 = EFL_OBJECT_OP_CREATE_OP_ID(class_id, j);
eina_spinlock_take(&_ops_storage_lock);
#ifndef _WIN32
eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op);
eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op2);
#else
eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op);
eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op2);
#endif
eina_spinlock_release(&_ops_storage_lock);
op_id++;
j ++;
}
#ifdef EO_DEBUG
DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
#endif
if (!_vtable_func_set(vtable, klass, override_class, op, op_desc->func, EINA_TRUE))
op2_class_id = EFL_OBJECT_OP_CLASS_PART(op2);
//in case we are having a function overwrite for a specific type, copy the relevant vtable
if (!hitmap[op2_class_id])
{
const Eo_Vtable_Node node = vtable->chain[op2_class_id];
_vtable_copy_node(&vtable->chain[op2_class_id], &node);
hitmap[op2_class_id] = EINA_TRUE;
}
if (!_vtable_func_set(vtable, klass, override_class, op2, op_desc->func, EINA_TRUE))
return EINA_FALSE;
}
return EINA_TRUE;
}
@ -821,6 +887,7 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
{
EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
Efl_Object_Ops empty_ops = { 0 };
Eina_Bool *hitmap;
// not likely so use goto to alleviate l1 instruction cache of rare code
if (klass->functions_set) goto err_funcs;
@ -832,11 +899,12 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
klass->ops_count = object_ops->count;
klass->base_id = _eo_ops_last_id;
_eo_ops_last_id += klass->ops_count + 1;
klass->class_id = _UNMASK_ID(klass->header.id) - 1;
_vtable_init(&klass->vtable, DICH_CHAIN1(_eo_ops_last_id) + 1);
_vtable_init(&klass->vtable);
hitmap = alloca(klass->vtable.size);
memset(hitmap, 0, klass->vtable.size);
/* Flatten the function array */
{
const _Efl_Class **mro_itr = klass->mro;
@ -844,11 +912,44 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
/* Skip ourselves. */
for ( mro_itr-- ; mro_itr > klass->mro ; mro_itr--)
_vtable_copy_all(&klass->vtable, &(*mro_itr)->vtable);
{
_vtable_merge_defined_api(&klass->vtable, &(*mro_itr)->vtable, hitmap);
}
/*add slots for the interfaces we are inheriting from*/
for (int i = 0; klass->extensions[i]; i++)
{
const _Efl_Class *ext = klass->extensions[i];
/*for all extensions of the class, ensure that *at least* empty vtables are available, so the efl_isa calls do succeed*/
_vtable_merge_empty(&klass->vtable, &ext->vtable, hitmap);
}
}
{
unsigned int i;
return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, 0, EINA_FALSE);
for (i = 0; i < object_ops->count; i++)
{
Efl_Object_Op op = _efl_object_api_op_id_get_internal(object_ops->descs[i].api_func);
if (op == EFL_NOOP) continue; //EFL_NOOP means that this function is not yet defined, this will be handled later
short class_id = EFL_OBJECT_OP_CLASS_PART(op);
if (klass->vtable.chain[class_id].count == 0)
{
const _Efl_Class *required_klass = _eo_classes[class_id];
/* in case this type is not already inherited, error on everything that is not a mixin */
if (klass->desc->type == EFL_CLASS_TYPE_MIXIN)
{
/* this is when a mixin implemets a regular api, we just prepare a empty node, the rest will be implemented later */
_vtable_prepare_empty_node(&klass->vtable, required_klass->vtable.chain[class_id].count, class_id);
}
else
{
ERR("There is an API implemented, whoms type is not part of this class. %s vs. %s", klass->desc->name, required_klass->desc->name);
_vtable_take_over(&klass->vtable, &required_klass->vtable);
}
}
}
}
return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, EINA_FALSE, klass->class_id, hitmap);
err_funcs:
ERR("Class %s already had its functions set..", klass->desc->name);
return EINA_FALSE;
@ -1098,8 +1199,8 @@ _eo_free(_Eo_Object *obj, Eina_Bool manual_free EINA_UNUSED)
#endif
if (_obj_is_override(obj))
{
_vtable_func_clean_all(obj->opt->vtable);
eina_freeq_ptr_main_add(obj->opt->vtable, free, 0);
if (obj->opt)
_vtable_free(obj->opt->vtable, &obj->klass->vtable);
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
}
@ -1190,22 +1291,6 @@ err_obj:
return 0;
}
static void
_vtable_init(Eo_Vtable *vtable, size_t size)
{
vtable->size = size;
vtable->chain = calloc(vtable->size, sizeof(*vtable->chain));
}
static void
_vtable_free(Eo_Vtable *vtable)
{
if (!vtable) return;
_vtable_func_clean_all(vtable);
eina_freeq_ptr_main_add(vtable, free, sizeof(*vtable));
}
static Eina_Bool
_eo_class_mro_has(const _Efl_Class *klass, const _Efl_Class *find)
{
@ -1360,8 +1445,8 @@ eo_class_free(_Efl_Class *klass)
{
if (klass->desc->class_destructor)
klass->desc->class_destructor(_eo_class_id_get(klass));
_vtable_func_clean_all(&klass->vtable);
_vtable_mro_free(klass);
_vtable_free(&klass->vtable, NULL);
}
EINA_TRASH_CLEAN(&klass->objects.trash, data)
@ -1376,32 +1461,6 @@ eo_class_free(_Efl_Class *klass)
eina_freeq_ptr_main_add(klass, free, 0);
}
/* Not really called, just used for the ptr... */
static void
_eo_class_isa_func(Eo *eo_id EINA_UNUSED, void *class_data EINA_UNUSED)
{
/* Do nonthing. */
}
static void
_eo_class_isa_recursive_set(_Efl_Class *klass, const _Efl_Class *cur)
{
const _Efl_Class **extn_itr;
_vtable_func_set(&klass->vtable, klass, NULL, cur->base_id + cur->ops_count,
_eo_class_isa_func, EINA_TRUE);
for (extn_itr = cur->extensions ; *extn_itr ; extn_itr++)
{
_eo_class_isa_recursive_set(klass, *extn_itr);
}
if (cur->parent)
{
_eo_class_isa_recursive_set(klass, cur->parent);
}
}
static inline void
_eo_classes_release(void)
{
@ -1710,12 +1769,6 @@ efl_class_new(const Efl_Class_Description *desc, const Efl_Class *parent_id, ...
efl_class_functions_set(_eo_class_id_get(klass), NULL, NULL);
}
/* Mark which classes we implement */
if (klass->vtable.size)
{
_eo_class_isa_recursive_set(klass, klass);
}
_eo_class_constructor(klass);
DBG("Finished building class '%s'", klass->desc->name);
@ -1732,32 +1785,41 @@ efl_object_override(Eo *eo_id, const Efl_Object_Ops *ops)
if (ops)
{
Eo_Vtable *vtable = obj->opt->vtable;
//copy all the vtable nodes that we are going to change later on
Eina_Bool *hitmap;
if (!vtable)
{
vtable = calloc(1, sizeof(*vtable));
_vtable_init(vtable, obj->klass->vtable.size);
_vtable_copy_all(vtable, &obj->klass->vtable);
_vtable_init(vtable);
_vtable_take_over(vtable, &obj->klass->vtable);
}
if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, 0, EINA_TRUE))
hitmap = alloca(vtable->size * sizeof(Eina_Bool));
memset(hitmap, 0, vtable->size);
if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, EINA_TRUE, obj->klass->class_id, hitmap))
{
ERR("Failed to override functions for %s@%p. All previous "
"overrides have been reset.", obj->klass->desc->name, eo_id);
if (obj->opt->vtable == vtable)
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
{
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
}
else
_vtable_free(vtable);
{
_vtable_free(vtable, &obj->klass->vtable);
}
goto err;
}
EO_OPTIONAL_COW_SET(obj, vtable, vtable);
}
else
{
if (obj->opt->vtable)
{
_vtable_free(obj->opt->vtable);
_vtable_free(obj->opt->vtable, &obj->klass->vtable);
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
}
}
@ -1791,10 +1853,10 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
EO_CLASS_POINTER_GOTO(eo_id, lookinto, err_class0);
const op_type_funcs *func = _vtable_func_get
(&lookinto->vtable, klass->base_id + klass->ops_count);
if (EINA_UNLIKELY(lookinto->vtable.size <= klass->class_id))
return EINA_FALSE;
return (func && (func->func == _eo_class_isa_func));;
return !!lookinto->vtable.chain[klass->class_id].funcs;
}
domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
@ -1813,15 +1875,17 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
const op_type_funcs *func = _vtable_func_get
(EO_VTABLE(obj), klass->base_id + klass->ops_count);
const Eo_Vtable vtable = obj->klass->vtable;
if (EINA_UNLIKELY(vtable.size <= klass->class_id))
return EINA_FALSE;
isa = !!vtable.chain[klass->class_id].funcs;
// Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
tdata->cache.isa_id = eo_id;
tdata->cache.klass = klass_id;
// Currently implemented by reusing the LAST op id. Just marking it with
// _eo_class_isa_func.
isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
tdata->cache.isa = isa;
}
else
{
@ -1841,15 +1905,15 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
EO_OBJ_POINTER_GOTO(eo_id, obj, err_shared_obj);
EO_CLASS_POINTER_GOTO(klass_id, klass, err_shared_class);
const op_type_funcs *func = _vtable_func_get
(EO_VTABLE(obj), klass->base_id + klass->ops_count);
if (EINA_UNLIKELY(obj->klass->vtable.size <= klass->class_id))
return EINA_FALSE;
isa = !!obj->klass->vtable.chain[klass->class_id].funcs;
// Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
tdata->cache.isa_id = eo_id;
tdata->cache.klass = klass_id;
// Currently implemented by reusing the LAST op id. Just marking it with
// _eo_class_isa_func.
isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
tdata->cache.isa = isa;
EO_OBJ_DONE(eo_id);
eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
}
@ -2326,7 +2390,6 @@ efl_object_init(void)
_eo_classes = NULL;
_eo_classes_last_id = EO_CLASS_IDS_FIRST - 1;
_eo_ops_last_id = EFL_OBJECT_OP_IDS_FIRST;
_eo_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
if (_eo_log_dom < 0)
{
@ -2384,12 +2447,6 @@ efl_object_init(void)
eina_tls_set(_eo_table_data, data);
_efl_object_main_thread = eina_thread_self();
#ifdef EO_DEBUG
/* Call it just for coverage purposes. Ugly I know, but I like it better than
* casting everywhere else. */
_eo_class_isa_func(NULL, NULL);
#endif
efl_object_optional_cow =
eina_cow_add("Efl Object Optional Data", sizeof(Efl_Object_Optional),
64, &efl_object_optional_cow_default, EINA_TRUE);

View File

@ -70,17 +70,14 @@ static inline void _eo_id_release(const Eo_Id obj_id);
void _eo_condtor_done(Eo *obj);
typedef struct _Dich_Chain1 Dich_Chain1;
typedef struct _Eo_Vtable_Node Eo_Vtable_Node;
typedef struct _Eo_Vtable
{
Dich_Chain1 *chain;
unsigned int size;
Eo_Vtable_Node *chain;
unsigned short size;
} Eo_Vtable;
/* Clean the vtable. */
void _vtable_func_clean_all(Eo_Vtable *vtable);
struct _Eo_Header
{
Eo_Id id;
@ -88,7 +85,7 @@ struct _Eo_Header
struct _Efl_Object_Optional
{
Eo_Vtable *vtable;
Eo_Vtable *vtable;
Eina_List *composite_objects;
Efl_Del_Intercept del_intercept;
};
@ -129,12 +126,6 @@ struct _Eo_Object
Eina_Bool ownership_track:1;
};
/* How we search and store the implementations in classes. */
#define DICH_CHAIN_LAST_BITS 5
#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS)
#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS)
#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1))
extern Eina_Cow *efl_object_optional_cow;
#define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); _cow; })
#define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE)
@ -146,6 +137,7 @@ extern Eina_Cow *efl_object_optional_cow;
EO_OPTIONAL_COW_END(_obj##_cow, _obj); \
}} while (0)
#define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
#define EO_VTABLE2(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data);
@ -155,15 +147,9 @@ typedef struct
const _Efl_Class *src;
} op_type_funcs;
typedef struct _Dich_Chain2
{
op_type_funcs funcs[DICH_CHAIN_LAST_SIZE];
unsigned short refcount;
} Dich_Chain2;
struct _Dich_Chain1
{
Dich_Chain2 *chain2;
struct _Eo_Vtable_Node{
op_type_funcs *funcs;
unsigned short count;
};
typedef struct
@ -203,7 +189,7 @@ struct _Efl_Class
} iterators;
unsigned int obj_size; /**< size of an object of this class */
unsigned int base_id;
unsigned int class_id; /**< the id which can be used to find the slot in _eo_classes and vtables chains */
unsigned int data_offset; /* < Offset of the data within object data. */
unsigned int ops_count; /* < Offset of the data within object data. */

View File

@ -329,7 +329,6 @@ EFL_END_TEST
EFL_START_TEST(efl_data_safe_fetch)
{
Eo *obj = efl_add_ref(SIMPLE2_CLASS, NULL);
fail_if(!obj || !efl_data_scope_safe_get(obj, SIMPLE2_CLASS));
efl_unref(obj);