diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h index 69fd84a560..6e30a29861 100644 --- a/src/lib/eo/Eo.h +++ b/src/lib/eo/Eo.h @@ -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. diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c index a5efc38e70..d2e190df3d 100644 --- a/src/lib/eo/eo.c +++ b/src/lib/eo/eo.c @@ -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 diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c index d14a70ca0c..9f50d57538 100644 --- a/src/lib/eo/eo_base_class.c +++ b/src/lib/eo/eo_base_class.c @@ -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), \