efl/src/lib/eo/eo_ptr_indirection.c

188 lines
5.8 KiB
C
Raw Normal View History

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "eo_ptr_indirection.h"
extern Eina_Thread _efl_object_main_thread;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
//////////////////////////////////////////////////////////////////////////
Eina_TLS _eo_table_data;
Eo_Id_Data *_eo_table_data_shared = NULL;
Eo_Id_Table_Data *_eo_table_data_shared_data = NULL;
eo - make eo id table TLS private data for thread safety and speed This moved all the eoid tables, eoid lookup caches, generation count information ad eo_isa cache into a TLS segment of memory that is thread private. There is also a shared domain for EO objects that all threads can access, but it has an added cost of a lock. This means objects accessed outside the thread they were created in cannot be accessed by another thread unless they are adopted in temporarily, or create4d with the shared domain active at the time of creation. child objects will use their parent object domain if created with a parent object passed in. If you were accessing EO (EFL) objects across threads before then this will actually now cause your code to fail as it was invalid before to do this as no actual objects were threadsafe in EFL, so this will force things to "fail early". ecore_thread_main_loop_begin() and end() still work as this uses the eo domain adoption features to temporarily adopt a domain during this section and then return it when done. This returns speed back to eo brining the overhead in my tests of lookup for the elm genlist autobounce test in elementary from about 5-7% down to 2.5-2.6%. A steep drop. This does not mean everything is perfect. Still to do are: 1. Tests in the test suite 2. Some API's to help for sending objects from thread to thread 3. Make the eo call cache TLS data to make it also safe 4. Look at other locks in eo and probably move them to TLS data 5. Make eo resolve and call wrappers that call the real method func do recursive mutex wrapping of the given object IF it is a shared object to provide threadsafety transparently for shared objects (but adding some overhead as a result) 6. Test test est, and that is why this commit is going in now for wider testing 7. Decide how to make this work with sending IPC (between threads) 8. Deciding what makes an object sendable (a sendable property in base?) 9. Deciding what makes an object shareable (a sharable property in base?)
2016-09-07 01:53:33 -07:00
//////////////////////////////////////////////////////////////////////////
void
_eo_pointer_error(const char *msg)
{
ERR("%s", msg);
}
#ifdef HAVE_EO_ID
static void
_eo_obj_pointer_invalid(const Eo_Id obj_id,
Eo_Id_Data *data,
unsigned char domain)
{
Eina_Thread thread = eina_thread_self();
const char *tself = "main";
const char *type = "object";
char tbuf[128];
if (obj_id & ((Eo_Id)1 << (REF_TAG_SHIFT - 1))) type = "class";
if (thread != _efl_object_main_thread)
{
snprintf(tbuf, sizeof(tbuf), "%p", (void *)thread);
tself = tbuf;
}
ERR("EOID %p is not a valid %s. "
"EOID domain=%i, current_domain=%i, local_domain=%i. "
"EOID generation=%lx, id=%lx, ref=%i, super=%i. "
"Thread self=%s. "
"Available domains [%s %s %s %s]. "
"Maybe it has been deleted or does not belong to your thread?",
(void *)obj_id,
type,
(int)domain,
(int)data->domain_stack[data->stack_top],
(int)data->local_domain,
(unsigned long)(obj_id & MASK_GENERATIONS),
(unsigned long)(obj_id >> SHIFT_ENTRY_ID) & (MAX_ENTRY_ID | MAX_TABLE_ID | MAX_MID_TABLE_ID),
(int)(obj_id >> REF_TAG_SHIFT) & 0x1,
(int)(obj_id >> SUPER_TAG_SHIFT) & 0x1,
tself,
(data->tables[0]) ? "0" : " ",
(data->tables[1]) ? "1" : " ",
(data->tables[2]) ? "2" : " ",
(data->tables[3]) ? "3" : " "
);
}
#endif
_Eo_Object *
_eo_obj_pointer_get(const Eo_Id obj_id)
{
#ifdef HAVE_EO_ID
_Eo_Id_Entry *entry;
Generation_Counter generation;
Table_Index mid_table_id, table_id, entry_id;
Eo_Id tag_bit;
Eo_Id_Data *data;
Eo_Id_Table_Data *tdata;
unsigned char domain;
// NULL objects will just be sensibly ignored. not worth complaining
// every single time.
data = _eo_table_data_get();
EINA_PREFETCH(&(data->tables[0]));
domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
tdata = _eo_table_data_table_get(data, domain);
EINA_PREFETCH(&(tdata->cache.id));
if (EINA_UNLIKELY(!tdata)) goto err;
if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
{
if (obj_id == tdata->cache.id)
return tdata->cache.object;
mid_table_id = (obj_id >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;
EINA_PREFETCH(&(tdata->eo_ids_tables[mid_table_id]));
table_id = (obj_id >> SHIFT_TABLE_ID) & MASK_TABLE_ID;
entry_id = (obj_id >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;
generation = obj_id & MASK_GENERATIONS;
// get tag bit to check later down below - pipelining
tag_bit = (obj_id) & MASK_OBJ_TAG;
if (!obj_id) goto err_null;
else if (!tag_bit) goto err;
// Check the validity of the entry
if (tdata->eo_ids_tables[mid_table_id])
{
_Eo_Ids_Table *tab = TABLE_FROM_IDS;
if (tab)
{
entry = &(tab->entries[entry_id]);
if (entry->active && (entry->generation == generation))
{
// Cache the result of that lookup
tdata->cache.object = entry->ptr;
tdata->cache.id = obj_id;
return entry->ptr;
}
}
}
goto err;
}
else
{
eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
if (obj_id == tdata->cache.id)
// yes we return keeping the lock locked. thats why
// you must call _eo_obj_pointer_done() wrapped
// by EO_OBJ_DONE() to release
return tdata->cache.object;
mid_table_id = (obj_id >> SHIFT_MID_TABLE_ID) & MASK_MID_TABLE_ID;
EINA_PREFETCH(&(tdata->eo_ids_tables[mid_table_id]));
table_id = (obj_id >> SHIFT_TABLE_ID) & MASK_TABLE_ID;
entry_id = (obj_id >> SHIFT_ENTRY_ID) & MASK_ENTRY_ID;
generation = obj_id & MASK_GENERATIONS;
// get tag bit to check later down below - pipelining
tag_bit = (obj_id) & MASK_OBJ_TAG;
if (!obj_id) goto err_shared_null;
else if (!tag_bit) goto err_shared;
// Check the validity of the entry
if (tdata->eo_ids_tables[mid_table_id])
{
_Eo_Ids_Table *tab = TABLE_FROM_IDS;
if (tab)
{
entry = &(tab->entries[entry_id]);
if (entry->active && (entry->generation == generation))
{
// Cache the result of that lookup
tdata->cache.object = entry->ptr;
tdata->cache.id = obj_id;
// yes we return keeping the lock locked. thats why
// you must call _eo_obj_pointer_done() wrapped
// by EO_OBJ_DONE() to release
return entry->ptr;
}
}
}
goto err_shared;
}
err_shared_null:
eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
err_null:
DBG("obj_id is NULL. Possibly unintended access?");
return NULL;
err_shared:
eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
err:
_eo_obj_pointer_invalid(obj_id, data, domain);
return NULL;
#else
return (_Eo_Object *) obj_id;
#endif
}
void
_eo_obj_pointer_done(const Eo_Id obj_id)
{
#ifdef HAVE_EO_ID
Efl_Id_Domain domain = (obj_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED)) return;
eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
#endif
}