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
|
||||
typedef struct _Efl_Object_Op_Call_Data
|
||||
{
|
||||
Eo *eo_id;
|
||||
_Eo_Object *obj;
|
||||
void *func;
|
||||
void *data;
|
||||
Eo *eo_id;
|
||||
_Eo_Object *obj;
|
||||
void *func;
|
||||
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;
|
||||
|
||||
#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;
|
||||
Eina_Bool is_obj;
|
||||
Eina_Bool is_override = EINA_FALSE;
|
||||
Eo_Id_Data *data;
|
||||
Efl_Id_Domain domain = 0;
|
||||
|
||||
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;
|
||||
|
||||
call->eo_id = eo_id;
|
||||
call->lock_data = NULL;
|
||||
|
||||
is_obj = _eo_is_a_obj(eo_id);
|
||||
|
||||
if (is_obj)
|
||||
{
|
||||
EO_OBJ_POINTER_RETURN_VAL(eo_id, _obj, EINA_FALSE);
|
||||
domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
|
||||
|
||||
obj = _obj;
|
||||
klass = _obj->klass;
|
||||
vtable = obj->vtable;
|
||||
|
||||
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
|
||||
* 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);
|
||||
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
EO_CLASS_POINTER_RETURN_VAL(eo_id, _klass, EINA_FALSE);
|
||||
klass = _klass;
|
||||
vtable = &klass->vtable;
|
||||
call->obj = NULL;
|
||||
call->data = NULL;
|
||||
// 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_klass;
|
||||
}
|
||||
ok_klass_back:
|
||||
|
||||
inputklass = main_klass = klass;
|
||||
|
||||
|
||||
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 (!cache->op) goto err_cache_op;
|
||||
|
||||
/* If we have a current class, we need to itr to the next. */
|
||||
if (cur_klass)
|
||||
{
|
||||
func = _eo_kls_itr_next(klass, cur_klass, cache->op);
|
||||
|
||||
if (!func)
|
||||
goto end;
|
||||
|
||||
klass = func->src;
|
||||
// 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_cur_klass;
|
||||
}
|
||||
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;
|
||||
call->func = func->func;
|
||||
if (is_obj)
|
||||
{
|
||||
call->data = (char *) obj + cache->off[i].off;
|
||||
}
|
||||
call->data = (char *)obj + cache->off[i].off;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
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))
|
||||
{
|
||||
call->func = func->func;
|
||||
|
||||
if (is_obj)
|
||||
{
|
||||
call->data = _efl_data_scope_get(obj, func->src);
|
||||
}
|
||||
if (is_obj) call->data = _efl_data_scope_get(obj, func->src);
|
||||
|
||||
# if EFL_OBJECT_CALL_CACHE_SIZE > 0
|
||||
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
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
if (func->src != NULL)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
if (func->src != NULL) goto err_func_src;
|
||||
|
||||
end:
|
||||
/* Try composite objects */
|
||||
|
@ -483,12 +472,10 @@ end:
|
|||
{
|
||||
_Eo_Object *emb_obj = _eo_obj_pointer_get((Eo_Id)emb_obj_id);
|
||||
|
||||
if (!emb_obj)
|
||||
continue;
|
||||
if (!emb_obj) continue;
|
||||
|
||||
func = _vtable_func_get(emb_obj->vtable, cache->op);
|
||||
if (func == NULL)
|
||||
continue;
|
||||
if (func == NULL) continue;
|
||||
|
||||
if (EINA_LIKELY(func->func && func->src))
|
||||
{
|
||||
|
@ -496,30 +483,68 @@ end:
|
|||
call->obj = _efl_ref(emb_obj);
|
||||
call->func = func->func;
|
||||
call->data = _efl_data_scope_get(emb_obj, func->src);
|
||||
|
||||
/* We reffed it above, but no longer need/use it. */
|
||||
_efl_unref(obj);
|
||||
|
||||
return EINA_TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If it's a do_super call. */
|
||||
if (cur_klass)
|
||||
{
|
||||
/* If it's a do_super call. */
|
||||
if (cur_klass)
|
||||
{
|
||||
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.",
|
||||
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);
|
||||
}
|
||||
ERR("in %s:%d: func '%s' (%d) could not be resolved for class '%s' for super of '%s'.",
|
||||
file, line, func_name, cache->op, main_klass->desc->name,
|
||||
cur_klass->desc->name);
|
||||
goto err;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -529,6 +554,10 @@ _efl_object_call_end(Efl_Object_Op_Call_Data *call)
|
|||
if (EINA_LIKELY(!!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;
|
||||
/* Optional lock around objects and eoid table - only used if shared */
|
||||
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? */
|
||||
Eina_Bool shared : 1;
|
||||
};
|
||||
|
@ -298,6 +300,12 @@ _eo_table_data_table_new(Efl_Id_Domain domain)
|
|||
free(tdata);
|
||||
return NULL;
|
||||
}
|
||||
if (!eina_lock_recursive_new(&(tdata->obj_lock)))
|
||||
{
|
||||
eina_spinlock_free(&(tdata->lock));
|
||||
free(tdata);
|
||||
return NULL;
|
||||
}
|
||||
tdata->shared = EINA_TRUE;
|
||||
}
|
||||
// XXX: randomize generation count and allocation methods
|
||||
|
@ -324,7 +332,11 @@ _eo_table_data_new(Efl_Id_Domain domain)
|
|||
static void
|
||||
_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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue