Eina: Add Eina_Promise/Eina_Future.

This commit adds a new promise/future API which aims to replace
efl_future.
This commit is contained in:
Guilherme Iscaro 2017-08-08 18:10:36 -03:00
parent 27cba99045
commit d30c0d4f03
12 changed files with 2807 additions and 1 deletions

View File

@ -10,6 +10,7 @@ lib/eina/eina_config.h
installed_einaheadersdir = $(includedir)/eina-@VMAJ@/eina
dist_installed_einaheaders_DATA = \
lib/eina/eina_promise.h \
lib/eina/eina_safety_checks.h \
lib/eina/eina_error.h \
lib/eina/eina_debug.h \
@ -150,6 +151,8 @@ lib/eina/eina_mempool.c \
lib/eina/eina_mmap.c \
lib/eina/eina_module.c \
lib/eina/eina_prefix.c \
lib/eina/eina_promise.c \
lib/eina/eina_promise_private.h \
lib/eina/eina_quad.c \
lib/eina/eina_quadtree.c \
lib/eina/eina_rbtree.c \

View File

@ -251,6 +251,7 @@ ecore_init(void)
if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
if (_ecore_fps_debug) _ecore_fps_debug_init();
if (!ecore_mempool_init()) goto shutdown_mempool;
if (!_ecore_event_init()) goto shutdown_mempool;
_ecore_main_loop_init();
vpath = efl_add(EFL_VPATH_CORE_CLASS, NULL);

View File

@ -48,6 +48,15 @@ struct _Ecore_Event
};
GENERIC_ALLOC_SIZE_DECLARE(Ecore_Event);
typedef struct _Ecore_Future_Schedule_Entry
{
Eina_Future_Schedule_Entry base;
Eina_Future_Scheduler_Cb cb;
Eina_Future *future;
Ecore_Event *event;
Eina_Value value;
} Ecore_Future_Schedule_Entry;
static int events_num = 0;
static Ecore_Event *events = NULL;
static Ecore_Event *event_current = NULL;
@ -68,6 +77,10 @@ static int event_filters_delete_me = 0;
static int event_id_max = ECORE_EVENT_COUNT;
static int ecore_raw_event_type = ECORE_EVENT_NONE;
static void *ecore_raw_event_event = NULL;
static Ecore_Event_Handler *future_handler = NULL;
static int ECORE_EV_FUTURE_ID = -1;
static Eina_Mempool *mp_future_schedule_entry = NULL;
static Eina_Bool shutting_down = EINA_FALSE;
static void _ecore_event_purge_deleted(void);
static void *_ecore_event_del(Ecore_Event *event);
@ -268,6 +281,100 @@ _ecore_event_handler_del(Ecore_Event_Handler *event_handler)
return event_handler->data;
}
static Eina_Bool
ecore_future_dispatched(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
Ecore_Future_Schedule_Entry *entry = event;
entry->event = NULL;
entry->cb(entry->future, entry->value);
return EINA_FALSE;
}
static void
ecore_future_free(void *user_data, void *func_data EINA_UNUSED)
{
Ecore_Future_Schedule_Entry *entry = user_data;
/*
In case entry->event is not NULL, it means
that ecore is shutting down. In this case,
we must cancel the future otherwise Eina may
try to use it and lead to crashes.
*/
if (entry->event)
{
eina_future_cancel(entry->future);
eina_value_flush(&entry->value);
}
eina_mempool_free(mp_future_schedule_entry, entry);
}
static Eina_Future_Schedule_Entry *
ecore_future_schedule(Eina_Future_Scheduler *sched, Eina_Future_Scheduler_Cb cb, Eina_Future *future, Eina_Value value)
{
Ecore_Future_Schedule_Entry *entry = eina_mempool_malloc(mp_future_schedule_entry,
sizeof(Ecore_Future_Schedule_Entry));
EINA_SAFETY_ON_NULL_RETURN_VAL(entry, NULL);
entry->base.scheduler = sched;
entry->cb = cb;
entry->future = future;
entry->value = value;
entry->event = ecore_event_add(ECORE_EV_FUTURE_ID, entry, ecore_future_free, entry);
EINA_SAFETY_ON_NULL_GOTO(entry->event, err);
return &entry->base;
err:
eina_mempool_free(mp_future_schedule_entry, entry);
return NULL;
}
static void
ecore_future_recall(Eina_Future_Schedule_Entry *s_entry)
{
if (shutting_down) return;
Ecore_Future_Schedule_Entry *entry = (Ecore_Future_Schedule_Entry *)s_entry;
EINA_SAFETY_ON_NULL_RETURN(entry->event);
ecore_event_del(entry->event);
eina_value_flush(&entry->value);
entry->event = NULL;
}
static Eina_Future_Scheduler ecore_future_scheduler = {
.schedule = ecore_future_schedule,
.recall = ecore_future_recall,
};
Eina_Future_Scheduler *
_ecore_event_future_scheduler_get(void)
{
return &ecore_future_scheduler;
}
Eina_Bool
_ecore_event_init(void)
{
const char *choice = getenv("EINA_MEMPOOL");
if ((!choice) || (!choice[0])) choice = "chained_mempool";
shutting_down = EINA_FALSE;
ECORE_EV_FUTURE_ID = ecore_event_type_new();
future_handler = ecore_event_handler_add(ECORE_EV_FUTURE_ID, ecore_future_dispatched, NULL);
EINA_SAFETY_ON_NULL_GOTO(future_handler, err_handler);
//FIXME: Is 512 too high?
mp_future_schedule_entry = eina_mempool_add(choice, "Ecore_Future_Event",
NULL, sizeof(Ecore_Future_Schedule_Entry),
512);
EINA_SAFETY_ON_NULL_GOTO(mp_future_schedule_entry, err_pool);
return EINA_TRUE;
err_pool:
ecore_event_handler_del(future_handler);
future_handler = NULL;
err_handler:
ECORE_EV_FUTURE_ID = -1;
return EINA_FALSE;
}
void
_ecore_event_shutdown(void)
{
@ -275,6 +382,9 @@ _ecore_event_shutdown(void)
Ecore_Event_Handler *eh;
Ecore_Event_Filter *ef;
shutting_down = EINA_TRUE;
ecore_event_handler_del(future_handler);
future_handler = NULL;
while (events) _ecore_event_del(events);
event_current = NULL;
for (i = 0; i < event_handlers_num; i++)
@ -301,6 +411,7 @@ _ecore_event_shutdown(void)
event_filters_delete_me = 0;
event_filter_current = NULL;
event_filter_event_current = NULL;
ECORE_EV_FUTURE_ID = -1;
}
int

View File

@ -3286,5 +3286,12 @@ _efl_loop_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
return &version;
}
EOLIAN static Eina_Future_Scheduler *
_efl_loop_future_scheduler_get(Eo *obj EINA_UNUSED,
Efl_Loop_Data *pd EINA_UNUSED)
{
return _ecore_event_future_scheduler_get();
}
#include "efl_loop.eo.c"

View File

@ -187,6 +187,10 @@ void _ecore_idle_enterer_call(Eo *loop);
void _ecore_idle_exiter_call(Eo *loop);
Eina_Future_Scheduler *_ecore_event_future_scheduler_get(void);
Eina_Bool _ecore_event_init(void);
void _ecore_event_shutdown(void);
int _ecore_event_exist(void);
Ecore_Event *_ecore_event_add(int type,
@ -382,6 +386,9 @@ void ecore_loop_promise_register(Efl_Loop *l, Efl_Promise *p);
void ecore_loop_promise_unregister(Efl_Loop *l, Efl_Promise *p);
void ecore_loop_promise_fulfill(Efl_Promise *p);
Eina_Bool efl_promise2_init(void);
void efl_promise2_shutdown(void);
// access to direct input cb
#define ECORE_EVAS_INTERNAL

View File

@ -1,4 +1,5 @@
import efl_types;
import eina_types;
struct Efl.Loop.Arguments {
[[EFL loop arguments data structure]]
@ -69,6 +70,18 @@ class Efl.Loop (Efl.Object)
@in exit_code: ubyte; [[Returned value by begin()]]
}
}
@property future_scheduler {
[[Gets the Eina_Future_Scheduler for a given mainloop.
The Eina_Future_Scheduler returned by this function
should be used for creating promises (eina_promise_new())
so then can properly schedule resolve/reject events.
]]
get {}
values {
scheduler: ptr(Eina.Future.Scheduler); [[The scheduler.]]
}
}
job {
[[Will execute that promise in the near future.]]
params {

View File

@ -273,6 +273,7 @@ extern "C" {
#include <eina_freeq.h>
#include <eina_slstr.h>
#include <eina_debug.h>
#include <eina_promise.h>
#undef EAPI
#define EAPI

View File

@ -159,6 +159,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
S(file);
S(safepointer);
S(slstr);
S(promise);
#undef S
struct eina_desc_setup
@ -205,6 +206,7 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
S(file),
S(safepointer),
S(slstr),
S(promise),
#undef S
};
static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
@ -305,7 +307,6 @@ eina_init(void)
eina_cpu_count_internal();
eina_log_timing(_eina_log_dom, EINA_LOG_STATE_STOP, EINA_LOG_STATE_INIT);
_eina_main_count = 1;
eina_evlog("-eina_init", NULL, 0.0, NULL);
return 1;

1230
src/lib/eina/eina_promise.c Normal file

File diff suppressed because it is too large Load Diff

1390
src/lib/eina/eina_promise.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,38 @@
#ifndef __EINA_PROMISE_PRIVATE_H__
#define __EINA_PROMISE_PRIVATE_H__
#define ERROR_DISPATCH(_cbs, _ret, _value, _data) \
do { \
Eina_Error ERROR_DISPATCH__err; \
if (!(_cbs)->error) (_ret) = (_value); \
else \
{ \
eina_value_get(&(_value), &ERROR_DISPATCH__err); \
(_ret) = (_cbs)->error((_data), ERROR_DISPATCH__err); \
} \
} while (0)
#define EASY_FUTURE_DISPATCH(_ret, _value, _dead_future, _cbs, _data) \
do { \
if ((_value).type == EINA_VALUE_TYPE_ERROR) ERROR_DISPATCH((_cbs), (_ret), (_value), (_data)); \
else \
{ \
if ((!(_cbs)->success_type) || ((_cbs)->success_type && (_value).type == (_cbs)->success_type)) \
{ \
if (!(_cbs)->success) (_ret) = (_value); /* pass thru */ \
else (_ret) = (_cbs)->success((_data), (_value)); \
} \
else \
{ \
Eina_Value EASY_FUTURE_DISPATCH__err = EINA_VALUE_EMPTY; \
ERR("Future %p, success cb: %p data: %p, expected success_type %p (%s), got %p (%s)", \
_dead_future, (_cbs)->success, (_data), \
(_cbs)->success_type, eina_value_type_name_get((_cbs)->success_type), \
(_value).type, (_value).type ? eina_value_type_name_get((_value).type) : NULL); \
if (eina_value_setup(&EASY_FUTURE_DISPATCH__err, EINA_VALUE_TYPE_ERROR)) eina_value_set(&EASY_FUTURE_DISPATCH__err, EINVAL); \
ERROR_DISPATCH((_cbs), (_ret), EASY_FUTURE_DISPATCH__err, (_data)); \
} \
} \
if ((_cbs)->free) (_cbs)->free((_data), _dead_future); \
} while(0)
#endif

View File

@ -62,3 +62,7 @@ struct @extern Eina.Rw_Slice {
}
struct @extern Eina.Value.Type; [[Eina value type]]
struct @extern Eina.Future.Scheduler; [[This struct is used as a bridge between Eina and the future scheduler.
By using the provided functions Eina can schedule futures resolutions,
rejections and cancelations to a safe context.]]