eo: add infrastructure to attach an Eina_Future_Scheduler to any source of event.

Reviewed-by: Marcel Hollerbach <mail@marcel-hollerbach.de>
Differential Revision: https://phab.enlightenment.org/D10480
This commit is contained in:
Cedric Bail 2019-10-16 16:47:10 -07:00 committed by Marcel Hollerbach
parent 2f1894d054
commit 474b1b2c2d
3 changed files with 237 additions and 2 deletions

View File

@ -317,6 +317,18 @@ EOAPI Eina_Bool efl_event_callback_priority_add(Eo *obj, const Efl_Event_Descrip
*/
EOAPI Eina_Bool efl_event_callback_del(Eo *obj, const Efl_Event_Description *desc, Efl_Event_Cb func, const void *user_data);
/**
* @brief Get the Eina_Future scheduler that trigger them on a specific set of events on an object.
*
* @param[in] obj The object that the scheduler is attached to.
* @param[in] array The events that when triggered on the object will trigger the Eina_Future.
*
* @return Return a scheduler that will trigger future exactly when the event are triggered.
*
* @note You must use EFL_SCHEDULER_ARRAY_DEFINE() to create the @p array.
*/
EOAPI Eina_Future_Scheduler *efl_event_future_scheduler_get(const Eo *obj, Efl_Callback_Array_Item *array);
/**
* @brief Add an array of callbacks created by @ref EFL_CALLBACKS_ARRAY_DEFINE
* for an event with a specific priority. The array need to be sorted with @ref
@ -1899,7 +1911,6 @@ EOAPI void efl_wref_add(Eo *obj, Efl_Object **wref);
*/
EOAPI void efl_wref_del(Eo *obj, Efl_Object **wref);
/**
* @brief Generic data with string key on an object.
*
@ -2183,6 +2194,32 @@ EAPI int efl_callbacks_cmp(const Efl_Callback_Array_Item *a, const Efl_Callback_
return internal; \
}
/**
* Helper for creating global scheduler arrays. The callback will be set by scheduler get.
* Problems occur here in windows where you can't declare a static array with
* external symbols in them. These addresses are only known at runtime.
* This also allows for automatic sorting for better performance.
*/
#define EFL_SCHEDULER_ARRAY_DEFINE(Name, ...) \
static Efl_Callback_Array_Item * \
Name(void) \
{ \
const Efl_Event_Description *tmp[] = { __VA_ARGS__ }; \
static Efl_Callback_Array_Item internal[EINA_C_ARRAY_LENGTH(tmp) + 1] = { { 0, 0 } }; \
\
if (internal[0].desc == NULL) \
{ \
unsigned int i; \
\
for (i = 0; i < EINA_C_ARRAY_LENGTH(tmp); i++) \
internal[i].desc = tmp[i]; \
\
qsort(internal, EINA_C_ARRAY_LENGTH(internal) - 1, sizeof (internal[0]), \
(int(*)(const void*,const void*)) efl_callbacks_cmp); \
} \
return internal; \
}
/**
* @def efl_event_callback_add(obj, desc, cb, data)
* Add a callback for an event.

View File

@ -2770,6 +2770,7 @@ efl_callbacks_cmp(const Efl_Callback_Array_Item *a, const Efl_Callback_Array_Ite
else return -1;
}
#ifdef EO_DEBUG
/* NOTE: cannot use ecore_time_get()! */
static inline double

View File

@ -40,6 +40,7 @@ typedef struct
Eina_Inlist *generic_data;
Eo ***wrefs;
Eina_Hash *providers;
Eina_Hash *schedulers;
} Efl_Object_Extension;
struct _Efl_Object_Data
@ -155,7 +156,8 @@ _efl_object_extension_noneed(Efl_Object_Data *pd)
(ext->generic_data) ||
(ext->wrefs) ||
(ext->composite_parent) ||
(ext->providers)) return;
(ext->providers) ||
(ext->schedulers)) return;
_efl_object_extension_free(pd->ext);
pd->ext = NULL;
}
@ -171,6 +173,12 @@ _efl_object_invalidate(Eo *obj_id, Efl_Object_Data *pd)
pd->ext->providers = NULL;
_efl_object_extension_noneed(pd);
}
if (pd->ext && pd->ext->schedulers)
{
eina_hash_free(pd->ext->schedulers);
pd->ext->schedulers = NULL;
_efl_object_extension_noneed(pd);
}
EO_OBJ_POINTER_RETURN(obj_id, obj);
@ -1183,6 +1191,7 @@ struct _Eo_Callback_Description
static Eina_Mempool *_eo_callback_mempool = NULL;
static Eina_Mempool *_efl_pending_future_mempool = NULL;
static Eina_Mempool *_efl_future_scheduler_entry_mempool = NULL;
static void
_eo_callback_free(Eo_Callback_Description *cb)
@ -1677,6 +1686,188 @@ EOAPI EFL_FUNC_BODYV(efl_event_callback_array_del,
const Efl_Callback_Array_Item *array,
const void *user_data);
typedef struct _Efl_Future_Scheduler Efl_Future_Scheduler;
typedef struct _Efl_Future_Scheduler_Entry Efl_Future_Scheduler_Entry;
struct _Efl_Future_Scheduler
{
Eina_Future_Scheduler scheduler;
const Efl_Callback_Array_Item *array;
const Eo *self;
Eina_List *futures;
Eina_Bool listener : 1;
};
struct _Efl_Future_Scheduler_Entry
{
Eina_Future_Schedule_Entry base;
Eina_Future_Scheduler_Cb cb;
Eina_Future *future;
Eina_Value value;
};
static Eina_Trash *schedulers_trash = NULL;
static unsigned char schedulers_count = 0;
static void
_future_scheduler_cleanup(Efl_Object_Data *pd)
{
if (eina_hash_population(pd->ext->schedulers)) return ;
eina_hash_free(pd->ext->schedulers);
pd->ext->schedulers = NULL;
_efl_object_extension_noneed(pd);
}
static void
_futures_dispatch_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Future_Scheduler *sched = data;
Eina_List *entries = sched->futures;
Efl_Future_Scheduler_Entry *entry;
sched->futures = NULL;
efl_event_callback_array_del((Eo *) sched->self, sched->array, sched);
sched->listener = EINA_FALSE;
// Now trigger callbacks
EINA_LIST_FREE(entries, entry)
{
entry->cb(entry->future, entry->value);
eina_mempool_free(_efl_future_scheduler_entry_mempool, entry);
}
}
static void
_futures_cancel_cb(void *data)
{
Efl_Future_Scheduler *sched = data;
Eina_List *entries = sched->futures;
Efl_Future_Scheduler_Entry *entry;
efl_event_callback_array_del((Eo *) sched->self, sched->array, sched);
sched->listener = EINA_FALSE;
sched->futures = NULL;
EINA_LIST_FREE(entries, entry)
{
eina_future_cancel(entry->future);
eina_value_flush(&entry->value);
eina_mempool_free(_efl_future_scheduler_entry_mempool, entry);
}
if (schedulers_count > 8)
{
free(sched);
}
else
{
eina_trash_push(&schedulers_trash, sched);
schedulers_count++;
}
}
static Eina_Future_Schedule_Entry *
_efl_event_future_scheduler(Eina_Future_Scheduler *s_sched,
Eina_Future_Scheduler_Cb cb,
Eina_Future *future,
Eina_Value value)
{
Efl_Future_Scheduler *sched = (Efl_Future_Scheduler *)s_sched;
Efl_Future_Scheduler_Entry *entry;
entry = eina_mempool_malloc(_efl_future_scheduler_entry_mempool, sizeof(*entry));
EINA_SAFETY_ON_NULL_RETURN_VAL(entry, NULL);
entry->base.scheduler = &sched->scheduler;
entry->cb = cb;
entry->future = future;
entry->value = value;
if (!sched->listener)
{
efl_event_callback_array_add((Eo *) sched->self, sched->array, sched);
sched->listener = EINA_TRUE;
}
sched->futures = eina_list_append(sched->futures, entry);
return &entry->base;
}
static void
_efl_event_future_recall(Eina_Future_Schedule_Entry *s_entry)
{
Efl_Future_Scheduler_Entry *entry = (Efl_Future_Scheduler_Entry *)s_entry;
Efl_Future_Scheduler *sched;
Eina_List *lookup;
sched = (Efl_Future_Scheduler *) entry->base.scheduler;
lookup = eina_list_data_find_list(sched->futures, entry);
if (!lookup) return;
sched->futures = eina_list_remove_list(sched->futures, lookup);
if (!sched->futures)
{
Efl_Object_Data *pd = efl_data_scope_get(sched->self, EFL_OBJECT_CLASS);
_future_scheduler_cleanup(pd);
}
eina_value_flush(&entry->value);
eina_mempool_free(_efl_future_scheduler_entry_mempool, entry);
}
EOLIAN static Eina_Future_Scheduler *
_efl_object_event_future_scheduler_get(const Eo *obj, Efl_Object_Data *pd, Efl_Callback_Array_Item *array)
{
Efl_Object_Extension *ext;
Efl_Future_Scheduler *sched;
unsigned int i;
if (!array) return NULL;
ext = _efl_object_extension_need(pd);
// First lookup for an existing scheduler that match the provided array
if (!ext->schedulers) ext->schedulers = eina_hash_pointer_new(_futures_cancel_cb);
sched = eina_hash_find(ext->schedulers, &array);
if (sched) return &sched->scheduler;
// Define all the callback in the array to point to our internal callback,
// making the array ready to use.
for (i = 0; array[i].desc; i++)
array[i].func = _futures_dispatch_cb;
if (schedulers_count)
{
// Take one out of the trash for faster cycling
sched = eina_trash_pop(&schedulers_trash);
schedulers_count--;
}
else
{
// Need to allocate a new scheduler as none are on standby.
sched = calloc(1, sizeof (Efl_Future_Scheduler));
}
sched->scheduler.schedule = _efl_event_future_scheduler;
sched->scheduler.recall = _efl_event_future_recall;
sched->array = array;
sched->self = obj;
eina_hash_add(ext->schedulers, &array, sched);
return &sched->scheduler;
}
EOAPI EFL_FUNC_BODYV_CONST(efl_event_future_scheduler_get,
Eina_Future_Scheduler *, 0, EFL_FUNC_CALL(array),
Efl_Callback_Array_Item *array);
static Eina_Bool
_cb_desc_match(const Efl_Event_Description *a, const Efl_Event_Description *b, Eina_Bool legacy_compare)
{
@ -2396,12 +2587,17 @@ _efl_object_class_constructor(Efl_Class *klass EINA_UNUSED)
eina_mempool_add("chained_mempool", NULL, NULL,
sizeof(Efl_Future_Pending), 256);
_efl_future_scheduler_entry_mempool =
eina_mempool_add("chained_mempool", NULL, NULL,
sizeof(Efl_Future_Scheduler_Entry), 256);
_eo_nostep_alloc = !!getenv("EO_NOSTEP_ALLOC");
}
EOLIAN static void
_efl_object_class_destructor(Efl_Class *klass EINA_UNUSED)
{
eina_mempool_del(_efl_future_scheduler_entry_mempool);
eina_mempool_del(_efl_pending_future_mempool);
eina_mempool_del(_eo_callback_mempool);
eina_hash_free(_legacy_events_hash);
@ -2414,6 +2610,7 @@ _efl_object_class_destructor(Efl_Class *klass EINA_UNUSED)
EFL_OBJECT_OP_FUNC(efl_event_callback_array_del, _efl_object_event_callback_array_del), \
EFL_OBJECT_OP_FUNC(efl_event_callback_call, _efl_object_event_callback_call), \
EFL_OBJECT_OP_FUNC(efl_event_callback_legacy_call, _efl_object_event_callback_legacy_call), \
EFL_OBJECT_OP_FUNC(efl_event_future_scheduler_get, _efl_object_event_future_scheduler_get), \
EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_object_dbg_info_get), \
EFL_OBJECT_OP_FUNC(efl_wref_add, _efl_object_wref_add), \
EFL_OBJECT_OP_FUNC(efl_wref_del, _efl_object_wref_del), \