forked from enlightenment/efl
eo - add single global mutex for all shared eo objects to they are th-safe
this adds a signle mutex (recursive) mutex for all eo objects that is auto-called by _efl_object_call_resolve() and _efl_object_call_end() that wrap all eo method calls and since its recursive it can be blindly called for sub-calls. this will lock all shared objects during any call to any shared object so only the thread calling now has access until it releases. not fine-grained but good enough and the best we can do "simplistically".
This commit is contained in:
parent
1d26e61478
commit
f695762d06
|
@ -714,10 +714,15 @@ EAPI Eina_Bool efl_compatible(const Eo *obj, const Eo *obj_target);
|
||||||
// to fetch internal function and object data at once
|
// to fetch internal function and object data at once
|
||||||
typedef struct _Efl_Object_Op_Call_Data
|
typedef struct _Efl_Object_Op_Call_Data
|
||||||
{
|
{
|
||||||
Eo *eo_id;
|
Eo *eo_id;
|
||||||
_Eo_Object *obj;
|
_Eo_Object *obj;
|
||||||
void *func;
|
void *func;
|
||||||
void *data;
|
void *data;
|
||||||
|
void *lock_data;
|
||||||
|
void *extn1; // for the future to avoid ABI issues
|
||||||
|
void *extn2; // for the future to avoid ABI issues
|
||||||
|
void *extn3; // for the future to avoid ABI issues
|
||||||
|
void *extn4; // for the future to avoid ABI issues
|
||||||
} Efl_Object_Op_Call_Data;
|
} Efl_Object_Op_Call_Data;
|
||||||
|
|
||||||
#define EFL_OBJECT_CALL_CACHE_SIZE 1
|
#define EFL_OBJECT_CALL_CACHE_SIZE 1
|
||||||
|
|
147
src/lib/eo/eo.c
147
src/lib/eo/eo.c
|
@ -334,6 +334,8 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
|
||||||
const op_type_funcs *func;
|
const op_type_funcs *func;
|
||||||
Eina_Bool is_obj;
|
Eina_Bool is_obj;
|
||||||
Eina_Bool is_override = EINA_FALSE;
|
Eina_Bool is_override = EINA_FALSE;
|
||||||
|
Eo_Id_Data *data;
|
||||||
|
Efl_Id_Domain domain = 0;
|
||||||
|
|
||||||
if (((Eo_Id) eo_id) & MASK_SUPER_TAG)
|
if (((Eo_Id) eo_id) & MASK_SUPER_TAG)
|
||||||
{
|
{
|
||||||
|
@ -348,18 +350,21 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
|
||||||
return EINA_FALSE;
|
return EINA_FALSE;
|
||||||
|
|
||||||
call->eo_id = eo_id;
|
call->eo_id = eo_id;
|
||||||
|
call->lock_data = NULL;
|
||||||
|
|
||||||
is_obj = _eo_is_a_obj(eo_id);
|
is_obj = _eo_is_a_obj(eo_id);
|
||||||
|
|
||||||
if (is_obj)
|
if (is_obj)
|
||||||
{
|
{
|
||||||
EO_OBJ_POINTER_RETURN_VAL(eo_id, _obj, EINA_FALSE);
|
EO_OBJ_POINTER_RETURN_VAL(eo_id, _obj, EINA_FALSE);
|
||||||
|
domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
|
||||||
|
|
||||||
obj = _obj;
|
obj = _obj;
|
||||||
klass = _obj->klass;
|
klass = _obj->klass;
|
||||||
vtable = obj->vtable;
|
vtable = obj->vtable;
|
||||||
|
|
||||||
if (_obj_is_override(obj) && cur_klass &&
|
if (_obj_is_override(obj) && cur_klass &&
|
||||||
(_eo_class_id_get(cur_klass) == EFL_OBJECT_OVERRIDE_CLASS))
|
(_eo_class_id_get(cur_klass) == EFL_OBJECT_OVERRIDE_CLASS))
|
||||||
{
|
{
|
||||||
/* Doing a efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS) should result in calling
|
/* Doing a efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS) should result in calling
|
||||||
* as if it's a normal class. */
|
* as if it's a normal class. */
|
||||||
|
@ -370,38 +375,36 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
|
||||||
is_override = _obj_is_override(obj) && (cur_klass == NULL);
|
is_override = _obj_is_override(obj) && (cur_klass == NULL);
|
||||||
|
|
||||||
call->obj = obj;
|
call->obj = obj;
|
||||||
|
if (EINA_UNLIKELY(domain == EFL_ID_DOMAIN_SHARED))
|
||||||
|
{
|
||||||
|
// YES this is a goto with a label to return. this is a
|
||||||
|
// micro-optimization to move infrequent code out of the
|
||||||
|
// hot path of the function
|
||||||
|
goto ok_lock;
|
||||||
|
}
|
||||||
|
ok_lock_back:
|
||||||
_efl_ref(_obj);
|
_efl_ref(_obj);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
EO_CLASS_POINTER_RETURN_VAL(eo_id, _klass, EINA_FALSE);
|
// YES this is a goto with a label to return. this is a
|
||||||
klass = _klass;
|
// micro-optimization to move infrequent code out of the
|
||||||
vtable = &klass->vtable;
|
// hot path of the function
|
||||||
call->obj = NULL;
|
goto ok_klass;
|
||||||
call->data = NULL;
|
|
||||||
}
|
}
|
||||||
|
ok_klass_back:
|
||||||
|
|
||||||
inputklass = main_klass = klass;
|
inputklass = main_klass = klass;
|
||||||
|
|
||||||
|
if (!cache->op) goto err_cache_op;
|
||||||
if (!cache->op)
|
|
||||||
{
|
|
||||||
ERR("%s:%d: unable to resolve %s api func '%s' in class '%s'.",
|
|
||||||
file, line, (!is_obj ? "class" : "regular"),
|
|
||||||
func_name, klass->desc->name);
|
|
||||||
|
|
||||||
return EINA_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we have a current class, we need to itr to the next. */
|
/* If we have a current class, we need to itr to the next. */
|
||||||
if (cur_klass)
|
if (cur_klass)
|
||||||
{
|
{
|
||||||
func = _eo_kls_itr_next(klass, cur_klass, cache->op);
|
// YES this is a goto with a label to return. this is a
|
||||||
|
// micro-optimization to move infrequent code out of the
|
||||||
if (!func)
|
// hot path of the function
|
||||||
goto end;
|
goto ok_cur_klass;
|
||||||
|
|
||||||
klass = func->src;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -421,29 +424,22 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
|
||||||
func = (const op_type_funcs *)cache->entry[i].func;
|
func = (const op_type_funcs *)cache->entry[i].func;
|
||||||
call->func = func->func;
|
call->func = func->func;
|
||||||
if (is_obj)
|
if (is_obj)
|
||||||
{
|
call->data = (char *)obj + cache->off[i].off;
|
||||||
call->data = (char *) obj + cache->off[i].off;
|
|
||||||
}
|
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
func = _vtable_func_get(vtable, cache->op);
|
func = _vtable_func_get(vtable, cache->op);
|
||||||
|
if (!func) goto end;
|
||||||
if (!func)
|
|
||||||
goto end;
|
|
||||||
}
|
}
|
||||||
|
ok_cur_klass_back:
|
||||||
|
|
||||||
if (EINA_LIKELY(func->func && func->src))
|
if (EINA_LIKELY(func->func && func->src))
|
||||||
{
|
{
|
||||||
call->func = func->func;
|
call->func = func->func;
|
||||||
|
|
||||||
if (is_obj)
|
if (is_obj) call->data = _efl_data_scope_get(obj, func->src);
|
||||||
{
|
|
||||||
call->data = _efl_data_scope_get(obj, func->src);
|
|
||||||
}
|
|
||||||
|
|
||||||
# if EFL_OBJECT_CALL_CACHE_SIZE > 0
|
# if EFL_OBJECT_CALL_CACHE_SIZE > 0
|
||||||
if (!cur_klass && !is_override)
|
if (!cur_klass && !is_override)
|
||||||
|
@ -461,17 +457,10 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (func->src != NULL)
|
if (func->src != NULL) goto err_func_src;
|
||||||
{
|
|
||||||
ERR("in %s:%d: you called a pure virtual func '%s' (%d) of class '%s'.",
|
|
||||||
file, line, func_name, cache->op, klass->desc->name);
|
|
||||||
return EINA_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
end:
|
end:
|
||||||
/* Try composite objects */
|
/* Try composite objects */
|
||||||
|
@ -483,12 +472,10 @@ end:
|
||||||
{
|
{
|
||||||
_Eo_Object *emb_obj = _eo_obj_pointer_get((Eo_Id)emb_obj_id);
|
_Eo_Object *emb_obj = _eo_obj_pointer_get((Eo_Id)emb_obj_id);
|
||||||
|
|
||||||
if (!emb_obj)
|
if (!emb_obj) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
func = _vtable_func_get(emb_obj->vtable, cache->op);
|
func = _vtable_func_get(emb_obj->vtable, cache->op);
|
||||||
if (func == NULL)
|
if (func == NULL) continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
if (EINA_LIKELY(func->func && func->src))
|
if (EINA_LIKELY(func->func && func->src))
|
||||||
{
|
{
|
||||||
|
@ -496,30 +483,68 @@ end:
|
||||||
call->obj = _efl_ref(emb_obj);
|
call->obj = _efl_ref(emb_obj);
|
||||||
call->func = func->func;
|
call->func = func->func;
|
||||||
call->data = _efl_data_scope_get(emb_obj, func->src);
|
call->data = _efl_data_scope_get(emb_obj, func->src);
|
||||||
|
|
||||||
/* We reffed it above, but no longer need/use it. */
|
/* We reffed it above, but no longer need/use it. */
|
||||||
_efl_unref(obj);
|
_efl_unref(obj);
|
||||||
|
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If it's a do_super call. */
|
||||||
|
if (cur_klass)
|
||||||
{
|
{
|
||||||
/* If it's a do_super call. */
|
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.",
|
||||||
if (cur_klass)
|
file, line, func_name, cache->op, main_klass->desc->name,
|
||||||
{
|
cur_klass->desc->name);
|
||||||
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.",
|
goto err;
|
||||||
file, line, func_name, cache->op, main_klass->desc->name,
|
|
||||||
cur_klass->desc->name);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* we should not be able to take this branch */
|
|
||||||
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s'.",
|
|
||||||
file, line, func_name, cache->op, main_klass->desc->name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* we should not be able to take this branch */
|
||||||
|
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s'.",
|
||||||
|
file, line, func_name, cache->op, main_klass->desc->name);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
err_cache_op:
|
||||||
|
ERR("%s:%d: unable to resolve %s api func '%s' in class '%s'.",
|
||||||
|
file, line, (!is_obj ? "class" : "regular"),
|
||||||
|
func_name, klass->desc->name);
|
||||||
|
goto err;
|
||||||
|
err_func_src:
|
||||||
|
ERR("in %s:%d: you called a pure virtual func '%s' (%d) of class '%s'.",
|
||||||
|
file, line, func_name, cache->op, klass->desc->name);
|
||||||
|
err:
|
||||||
|
if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return EINA_FALSE;
|
||||||
|
eina_lock_take(&(data->tables[EFL_ID_DOMAIN_SHARED]->obj_lock));
|
||||||
|
return EINA_FALSE;
|
||||||
|
|
||||||
|
// yes - special "move out of hot path" code blobs with goto's for
|
||||||
|
// speed reasons to have intr prefetches work better and miss less
|
||||||
|
ok_cur_klass:
|
||||||
|
{
|
||||||
|
func = _eo_kls_itr_next(klass, cur_klass, cache->op);
|
||||||
|
if (!func) goto end;
|
||||||
|
klass = func->src;
|
||||||
|
}
|
||||||
|
goto ok_cur_klass_back;
|
||||||
|
|
||||||
|
ok_klass:
|
||||||
|
{
|
||||||
|
EO_CLASS_POINTER_RETURN_VAL(eo_id, _klass, EINA_FALSE);
|
||||||
|
klass = _klass;
|
||||||
|
vtable = &klass->vtable;
|
||||||
|
call->obj = NULL;
|
||||||
|
call->data = NULL;
|
||||||
|
}
|
||||||
|
goto ok_klass_back;
|
||||||
|
|
||||||
|
ok_lock:
|
||||||
|
{
|
||||||
|
data = call->lock_data = _eo_table_data_get();
|
||||||
|
eina_lock_take(&(data->tables[EFL_ID_DOMAIN_SHARED]->obj_lock));
|
||||||
|
}
|
||||||
|
goto ok_lock_back;
|
||||||
|
|
||||||
return EINA_FALSE;
|
return EINA_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -529,6 +554,10 @@ _efl_object_call_end(Efl_Object_Op_Call_Data *call)
|
||||||
if (EINA_LIKELY(!!call->obj))
|
if (EINA_LIKELY(!!call->obj))
|
||||||
{
|
{
|
||||||
_efl_unref(call->obj);
|
_efl_unref(call->obj);
|
||||||
|
if (EINA_LIKELY(!call->lock_data)) return;
|
||||||
|
|
||||||
|
Eo_Id_Data *data = call->lock_data;
|
||||||
|
eina_lock_release(&(data->tables[EFL_ID_DOMAIN_SHARED]->obj_lock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -269,6 +269,8 @@ struct _Eo_Id_Table_Data
|
||||||
Generation_Counter generation;
|
Generation_Counter generation;
|
||||||
/* Optional lock around objects and eoid table - only used if shared */
|
/* Optional lock around objects and eoid table - only used if shared */
|
||||||
Eina_Spinlock lock;
|
Eina_Spinlock lock;
|
||||||
|
/* Optional lock around all objects in eoid table - only used if shared */
|
||||||
|
Eina_Lock obj_lock;
|
||||||
/* are we shared so we need lock/unlock? */
|
/* are we shared so we need lock/unlock? */
|
||||||
Eina_Bool shared : 1;
|
Eina_Bool shared : 1;
|
||||||
};
|
};
|
||||||
|
@ -298,6 +300,12 @@ _eo_table_data_table_new(Efl_Id_Domain domain)
|
||||||
free(tdata);
|
free(tdata);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (!eina_lock_recursive_new(&(tdata->obj_lock)))
|
||||||
|
{
|
||||||
|
eina_spinlock_free(&(tdata->lock));
|
||||||
|
free(tdata);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
tdata->shared = EINA_TRUE;
|
tdata->shared = EINA_TRUE;
|
||||||
}
|
}
|
||||||
// XXX: randomize generation count and allocation methods
|
// XXX: randomize generation count and allocation methods
|
||||||
|
@ -324,7 +332,11 @@ _eo_table_data_new(Efl_Id_Domain domain)
|
||||||
static void
|
static void
|
||||||
_eo_table_data_table_free(Eo_Id_Table_Data *tdata)
|
_eo_table_data_table_free(Eo_Id_Table_Data *tdata)
|
||||||
{
|
{
|
||||||
if (tdata->shared) eina_spinlock_free(&(tdata->lock));
|
if (tdata->shared)
|
||||||
|
{
|
||||||
|
eina_spinlock_free(&(tdata->lock));
|
||||||
|
eina_lock_free(&(tdata->obj_lock));
|
||||||
|
}
|
||||||
free(tdata);
|
free(tdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue