summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuilherme Iscaro <iscaro@profusion.mobi>2017-08-08 18:10:36 -0300
committerGuilherme Iscaro <iscaro@profusion.mobi>2017-09-04 10:24:00 -0300
commit5bd8c9a78d7bc21a956b775fb250e8e14711d5b9 (patch)
tree5a3b270ecd53a712b66ad32e2989091250418423
parentc9a0237770a7fb0f1d94c9f99b7cab68399a922f (diff)
downloadefl-5bd8c9a78d7bc21a956b775fb250e8e14711d5b9.tar.gz
Eina: Add Eina_Promise/Eina_Future.
This commit adds a new promise/future API which aims to replace efl_future.
-rw-r--r--src/Makefile_Eina.am3
-rw-r--r--src/lib/ecore/ecore.c1
-rw-r--r--src/lib/ecore/ecore_events.c111
-rw-r--r--src/lib/ecore/ecore_main.c7
-rw-r--r--src/lib/ecore/ecore_private.h4
-rw-r--r--src/lib/ecore/efl_loop.eo13
-rw-r--r--src/lib/eina/Eina.h1
-rw-r--r--src/lib/eina/eina_main.c2
-rw-r--r--src/lib/eina/eina_promise.c1355
-rw-r--r--src/lib/eina/eina_promise.h1674
-rw-r--r--src/lib/eina/eina_promise_private.h38
-rw-r--r--src/lib/eo/eina_types.eot81
12 files changed, 3290 insertions, 0 deletions
diff --git a/src/Makefile_Eina.am b/src/Makefile_Eina.am
index 544ce0feda..bd74cdcb64 100644
--- a/src/Makefile_Eina.am
+++ b/src/Makefile_Eina.am
@@ -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 \
diff --git a/src/lib/ecore/ecore.c b/src/lib/ecore/ecore.c
index aa5e4e4f3c..ec733b20c6 100644
--- a/src/lib/ecore/ecore.c
+++ b/src/lib/ecore/ecore.c
@@ -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);
diff --git a/src/lib/ecore/ecore_events.c b/src/lib/ecore/ecore_events.c
index 3b13ef9015..ed405edb39 100644
--- a/src/lib/ecore/ecore_events.c
+++ b/src/lib/ecore/ecore_events.c
@@ -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
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
index 12e8cf450f..0bb15e0c02 100644
--- a/src/lib/ecore/ecore_main.c
+++ b/src/lib/ecore/ecore_main.c
@@ -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"
diff --git a/src/lib/ecore/ecore_private.h b/src/lib/ecore/ecore_private.h
index de6838b8b2..6437b41fb2 100644
--- a/src/lib/ecore/ecore_private.h
+++ b/src/lib/ecore/ecore_private.h
@@ -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,
diff --git a/src/lib/ecore/efl_loop.eo b/src/lib/ecore/efl_loop.eo
index aa8ae08d4c..e34deb586f 100644
--- a/src/lib/ecore/efl_loop.eo
+++ b/src/lib/ecore/efl_loop.eo
@@ -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 {
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index ee659117ea..76b6d0adf6 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -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
diff --git a/src/lib/eina/eina_main.c b/src/lib/eina/eina_main.c
index 36177158c0..38865eb57d 100644
--- a/src/lib/eina/eina_main.c
+++ b/src/lib/eina/eina_main.c
@@ -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) /
diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c
new file mode 100644
index 0000000000..596149fb10
--- /dev/null
+++ b/src/lib/eina/eina_promise.c
@@ -0,0 +1,1355 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_private.h"
+#include "eina_promise.h"
+#include "eina_mempool.h"
+#include "eina_promise_private.h"
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#define EINA_FUTURE_DISPATCHED ((Eina_Future_Cb)(0x01))
+
+#define EFL_MEMPOOL_CHECK_RETURN(_type, _mp, _p) \
+ if (!eina_mempool_from((_mp), (_p))) \
+ { \
+ ERR("The %s %p is not alive at mempool %p", (_type), (_p), (_mp)); \
+ return; \
+ }
+
+#define EFL_MEMPOOL_CHECK_RETURN_VAL(_type, _mp, _p, _val) \
+ if (!eina_mempool_from((_mp), (_p))) \
+ { \
+ ERR("The %s %p is not alive at mempool %p", (_type),(_p), (_mp)); \
+ return (_val); \
+ }
+
+#define EFL_MEMPOOL_CHECK_GOTO(_type, _mp, _p, _goto) \
+ if (!eina_mempool_from((_mp), (_p))) \
+ { \
+ ERR("The %s %p is not alive at mempool %p", (_type), (_p), (_mp)); \
+ goto _goto; \
+ }
+
+#define EINA_PROMISE_CHECK_RETURN(_p) \
+ do { \
+ EINA_SAFETY_ON_NULL_RETURN((_p)); \
+ EFL_MEMPOOL_CHECK_RETURN("promise", _promise_mp, (_p)); \
+ } while (0);
+
+#define EINA_PROMISE_CHECK_RETURN_VAL(_p, _val) \
+ do { \
+ EINA_SAFETY_ON_NULL_RETURN_VAL((_p), (_val)); \
+ EFL_MEMPOOL_CHECK_RETURN_VAL("promise", _promise_mp, (_p), (_val)); \
+ } while (0);
+
+#define EINA_PROMISE_CHECK_GOTO(_p, _goto) \
+ do { \
+ EINA_SAFETY_ON_NULL_GOTO((_p), _goto); \
+ EFL_MEMPOOL_CHECK_GOTO("promise", _promise_mp, (_p), _goto); \
+ } while (0);
+
+#define EINA_FUTURE_CHECK_GOTO(_p, _goto) \
+ do { \
+ EINA_SAFETY_ON_NULL_GOTO((_p), _goto); \
+ EFL_MEMPOOL_CHECK_GOTO("future", _future_mp, (_p), _goto); \
+ } while (0);
+
+#define EINA_FUTURE_CHECK_RETURN(_p) \
+ do { \
+ EINA_SAFETY_ON_NULL_RETURN((_p)); \
+ EFL_MEMPOOL_CHECK_RETURN("future", _future_mp, (_p)); \
+ if (_p->cb == EINA_FUTURE_DISPATCHED) \
+ { \
+ ERR("Future %p already dispatched", _p); \
+ return; \
+ } \
+ } while (0);
+
+#define EINA_FUTURE_CHECK_RETURN_VAL(_p, _val) \
+ do { \
+ EINA_SAFETY_ON_NULL_RETURN_VAL((_p), (_val)); \
+ EFL_MEMPOOL_CHECK_RETURN_VAL("future", _future_mp, (_p), (_val)); \
+ if (_p->cb == EINA_FUTURE_DISPATCHED) \
+ { \
+ ERR("Future %p already dispatched", _p); \
+ return (_val); \
+ } \
+ } while (0);
+
+#undef ERR
+#define ERR(...) EINA_LOG_DOM_ERR(_promise_log_dom, __VA_ARGS__)
+
+#undef DBG
+#define DBG(...) EINA_LOG_DOM_DBG(_promise_log_dom, __VA_ARGS__)
+
+#undef INF
+#define INF(...) EINA_LOG_DOM_INFO(_promise_log_dom, __VA_ARGS__)
+
+#undef WRN
+#define WRN(...) EINA_LOG_DOM_WARN(_promise_log_dom, __VA_ARGS__)
+
+#undef CRI
+#define CRI(...) EINA_LOG_DOM_CRIT(_promise_log_dom, __VA_ARGS__)
+
+#define _eina_promise_value_dbg(_msg, _p, _v) __eina_promise_value_dbg(_msg, _p, _v, __LINE__, __FUNCTION__)
+
+struct _Eina_Promise {
+ Eina_Future *future;
+ Eina_Future_Scheduler *scheduler;
+ Eina_Promise_Cancel_Cb cancel;
+ const void *data;
+};
+
+struct _Eina_Future {
+ Eina_Promise *promise;
+ Eina_Future *next;
+ Eina_Future *prev;
+ Eina_Future_Cb cb;
+ const void *data;
+ Eina_Future **storage;
+ Eina_Future_Schedule_Entry *scheduled_entry;
+};
+
+static Eina_Mempool *_promise_mp = NULL;
+static Eina_Mempool *_future_mp = NULL;
+static Eina_List *_pending_futures = NULL;
+static int _promise_log_dom = -1;
+
+static void _eina_promise_cancel(Eina_Promise *p);
+
+static Eina_Value_Struct_Member RACE_STRUCT_MEMBERS[] = {
+ EINA_VALUE_STRUCT_MEMBER(NULL, Eina_Future_Race_Result, value),
+ EINA_VALUE_STRUCT_MEMBER(NULL, Eina_Future_Race_Result, index),
+ EINA_VALUE_STRUCT_MEMBER_SENTINEL
+};
+
+static const Eina_Value_Struct_Desc RACE_STRUCT_DESC = {
+ .version = EINA_VALUE_STRUCT_DESC_VERSION,
+ .ops = NULL,
+ .members = RACE_STRUCT_MEMBERS,
+ .member_count = 2,
+ .size = sizeof(Eina_Future_Race_Result)
+};
+
+EAPI const Eina_Value_Struct_Desc *EINA_PROMISE_RACE_STRUCT_DESC = &RACE_STRUCT_DESC;
+
+static inline void
+__eina_promise_value_dbg(const char *msg,
+ const Eina_Promise *p,
+ const Eina_Value v,
+ int line,
+ const char *fname)
+{
+ if (EINA_UNLIKELY(eina_log_domain_level_check(_promise_log_dom,
+ EINA_LOG_LEVEL_DBG)))
+ {
+ if (!v.type)
+ {
+ eina_log_print(_promise_log_dom, EINA_LOG_LEVEL_DBG,
+ __FILE__, fname, line, "%s: %p with no value",
+ msg, p);
+ }
+ else
+ {
+ char *str = eina_value_to_string(&v);
+ eina_log_print(_promise_log_dom, EINA_LOG_LEVEL_DBG,
+ __FILE__, fname, line,
+ "%s: %p - Value Type: %s Content: %s", msg, p,
+ eina_value_type_name_get(eina_value_type_get(&v)),
+ str);
+ free(str);
+ }
+ }
+}
+
+static Eina_Bool
+_promise_setup(const Eina_Value_Type *type EINA_UNUSED, void *mem)
+{
+ Eina_Promise **tmem = mem;
+ *tmem = NULL;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_promise_flush(const Eina_Value_Type *type EINA_UNUSED, void *mem)
+{
+ Eina_Promise **tmem = mem;
+ if (*tmem)
+ {
+ _eina_promise_cancel(*tmem);
+ *tmem = NULL;
+ }
+ return EINA_TRUE;
+}
+
+static void
+_promise_replace(Eina_Promise **dst, Eina_Promise * const *src)
+{
+ if (*src == *dst) return;
+ if (*dst) _eina_promise_cancel(*dst);
+ *dst = *src;
+}
+
+static Eina_Bool
+_promise_vset(const Eina_Value_Type *type EINA_UNUSED, void *mem, va_list args)
+{
+ Eina_Promise **dst = mem;
+ Eina_Promise **src = va_arg(args, Eina_Promise **);
+ _promise_replace(dst, src);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_promise_pset(const Eina_Value_Type *type EINA_UNUSED,
+ void *mem, const void *ptr)
+{
+ Eina_Promise **dst = mem;
+ Eina_Promise * const *src = ptr;
+ _promise_replace(dst, src);
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_promise_pget(const Eina_Value_Type *type EINA_UNUSED,
+ const void *mem, void *ptr)
+{
+ Eina_Promise * const *src = mem;
+ Eina_Promise **dst = ptr;
+ *dst = *src;
+ return EINA_TRUE;
+}
+
+static Eina_Bool
+_promise_convert_to(const Eina_Value_Type *type EINA_UNUSED, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
+{
+ Eina_Promise * const *p = type_mem;
+
+ if (convert == EINA_VALUE_TYPE_STRINGSHARE ||
+ convert == EINA_VALUE_TYPE_STRING)
+ {
+ const char *other_mem;
+ char buf[128];
+ snprintf(buf, sizeof(buf), "Promise %p (cancel: %p, data: %p)",
+ *p, (*p)->cancel, (*p)->data);
+ other_mem = buf;
+ return eina_value_type_pset(convert, convert_mem, &other_mem);
+ }
+ return EINA_FALSE;
+}
+
+static const Eina_Value_Type EINA_VALUE_TYPE_PROMISE = {
+ .version = EINA_VALUE_TYPE_VERSION,
+ .value_size = sizeof(Eina_Promise *),
+ .name = "Eina_Promise",
+ .setup = _promise_setup,
+ .flush = _promise_flush,
+ .copy = NULL,
+ .compare = NULL,
+ .convert_to = _promise_convert_to,
+ .convert_from = NULL,
+ .vset = _promise_vset,
+ .pset = _promise_pset,
+ .pget = _promise_pget
+};
+
+static Eina_Promise *
+_eina_value_promise_steal(Eina_Value *value)
+{
+ Eina_Promise **p, *r;
+ /*
+ Do not use eina_value_flush()/eina_value_pset() in here,
+ otherwise it would cancel the promise!
+ */
+ p = eina_value_memory_get(value);
+ r = *p;
+ *p = NULL;
+ return r;
+}
+
+static Eina_Future *
+_eina_future_free(Eina_Future *f)
+{
+ DBG("Free future %p", f);
+ Eina_Future *next = f->next;
+ Eina_Future *prev = f->prev;
+ if (next) next->prev = NULL;
+ if (prev) prev->next = NULL;
+ eina_mempool_free(_future_mp, f);
+ return next;
+}
+
+static void
+_eina_promise_unlink(Eina_Promise *p)
+{
+ if (p->future)
+ {
+ DBG("Unliking promise %p and future %p", p, p->future);
+ p->future->promise = NULL;
+ p->future = NULL;
+ }
+}
+
+static void
+_eina_promise_link(Eina_Promise *p, Eina_Future *f)
+{
+ assert(f != NULL);
+ if (p) p->future = f;
+ f->promise = p;
+ DBG("Linking future %p with promise %p", f, p);
+}
+
+static void
+_eina_promise_cancel(Eina_Promise *p)
+{
+ DBG("Cancelling promise: %p, data: %p, future: %p", p, p->data, p->future);
+ _eina_promise_unlink(p);
+ p->cancel((void *)p->data, p);
+ eina_mempool_free(_promise_mp, p);
+}
+
+static void
+_eina_promise_value_steal_and_link(Eina_Value value, Eina_Future *f)
+{
+ Eina_Promise *p = _eina_value_promise_steal(&value);
+ DBG("Promise %p stolen from value", p);
+ eina_value_flush(&value);
+ if (f) _eina_promise_link(p, f);
+ else _eina_promise_unlink(p);
+}
+
+static Eina_Value
+_eina_future_cb_dispatch(Eina_Future *f, const Eina_Value value)
+{
+ Eina_Future_Cb cb = f->cb;
+
+ f->cb = EINA_FUTURE_DISPATCHED;
+ if (f->storage) *f->storage = NULL;
+
+ if (EINA_UNLIKELY(eina_log_domain_level_check(_promise_log_dom, EINA_LOG_LEVEL_DBG)))
+ {
+ if (!value.type) DBG("Distach cb: %p, data: %p with no value", cb, f->data);
+ else
+ {
+ char *str = eina_value_to_string(&value);
+ DBG("Dispatch cb: %p, data: %p, value type: %s content: '%s'",
+ cb, f->data,
+ eina_value_type_name_get(eina_value_type_get(&value)), str);
+ free(str);
+ }
+ }
+ return cb((void *)f->data, value, f);
+}
+
+static Eina_Value
+_eina_future_dispatch_internal(Eina_Future **f,
+ const Eina_Value value)
+{
+ Eina_Value next_value = EINA_VALUE_EMPTY;
+
+ assert(value.type != &EINA_VALUE_TYPE_PROMISE);
+ while ((*f) && (!(*f)->cb)) *f = _eina_future_free(*f);
+ if (!*f)
+ {
+ _eina_promise_value_dbg("No future to deliver value", NULL, value);
+ return value;
+ }
+ next_value = _eina_future_cb_dispatch(*f, value);
+ *f = _eina_future_free(*f);
+ return next_value;
+}
+
+static Eina_Bool
+_eina_value_is(const Eina_Value v1, const Eina_Value v2)
+{
+ if (v1.type != v2.type) return EINA_FALSE;
+ //Both types are NULL at this point... so they are equal...
+ if (!v1.type) return EINA_TRUE;
+ return !memcmp(eina_value_memory_get(&v1),
+ eina_value_memory_get(&v2),
+ v1.type->value_size);
+}
+
+static void
+_eina_future_dispatch(Eina_Future *f, Eina_Value value)
+{
+ Eina_Value next_value = _eina_future_dispatch_internal(&f, value);
+ if (!_eina_value_is(next_value, value)) eina_value_flush(&value);
+ if (!f)
+ {
+ if (next_value.type == &EINA_VALUE_TYPE_PROMISE)
+ {
+ DBG("There are no more futures, but next_value is a promise setting p->future to NULL.");
+ _eina_promise_value_steal_and_link(next_value, NULL);
+ }
+ else eina_value_flush(&next_value);
+ return;
+ }
+
+ if (next_value.type == &EINA_VALUE_TYPE_PROMISE)
+ {
+ if (EINA_UNLIKELY(eina_log_domain_level_check(_promise_log_dom, EINA_LOG_LEVEL_DBG)))
+ {
+ Eina_Promise *p = NULL;
+
+ eina_value_pget(&next_value, &p);
+ DBG("Future %p will wait for a new promise %p", f, p);
+ }
+ _eina_promise_value_steal_and_link(next_value, f);
+ }
+ else _eina_future_dispatch(f, next_value);
+ }
+
+static void
+_scheduled_entry_cb(Eina_Future *f, Eina_Value value)
+{
+ _pending_futures = eina_list_remove(_pending_futures, f);
+ f->scheduled_entry = NULL;
+ _eina_future_dispatch(f, value);
+}
+
+void
+eina_future_schedule_entry_recall(Eina_Future_Schedule_Entry *entry)
+{
+ entry->scheduler->recall(entry);
+}
+
+static void
+_eina_future_cancel(Eina_Future *f, int err)
+{
+ Eina_Value value = EINA_VALUE_EMPTY;
+
+ DBG("Cancelling future %p, cb: %p data: %p with error: %d - msg: '%s'",
+ f, f->cb, f->data, err, eina_error_msg_get(err));
+
+ for (; f->prev != NULL; f = f->prev)
+ {
+ assert(f->promise == NULL); /* intermediate futures shouldn't have a promise */
+ assert(f->scheduled_entry == NULL); /* intermediate futures shouldn't have pending dispatch */
+ }
+
+ if (f->scheduled_entry)
+ {
+ eina_future_schedule_entry_recall(f->scheduled_entry);
+ f->scheduled_entry = NULL;
+ _pending_futures = eina_list_remove(_pending_futures, f);
+ }
+
+ if (f->promise)
+ {
+ _eina_promise_cancel(f->promise);
+ f->promise = NULL;
+ }
+
+ eina_value_setup(&value, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&value, err);
+
+ while (f)
+ {
+ if (f->cb)
+ {
+ Eina_Value r = _eina_future_cb_dispatch(f, value);
+ if (!_eina_value_is(value, r)) eina_value_flush(&r);
+ }
+ f = _eina_future_free(f);
+ }
+ eina_value_flush(&value);
+}
+
+static void
+_eina_future_schedule(Eina_Promise *p,
+ Eina_Future *f,
+ Eina_Value value)
+{
+ f->scheduled_entry = p->scheduler->schedule(p->scheduler,
+ _scheduled_entry_cb,
+ f, value);
+ EINA_SAFETY_ON_NULL_GOTO(f->scheduled_entry, err);
+ assert(f->scheduled_entry->scheduler != NULL);
+ _pending_futures = eina_list_append(_pending_futures, f);
+ DBG("The promise %p schedule the future %p with cb: %p and data: %p",
+ p, f, f->cb, f->data);
+ return;
+ err:
+ _eina_future_cancel(p->future, ENOMEM);
+ eina_value_flush(&value);
+}
+
+static void
+_eina_promise_deliver(Eina_Promise *p,
+ Eina_Value value)
+{
+ if (p->future)
+ {
+ Eina_Future *f = p->future;
+ _eina_promise_unlink(p);
+ if (value.type == &EINA_VALUE_TYPE_PROMISE) _eina_promise_value_steal_and_link(value, f);
+ else _eina_future_schedule(p, f, value);
+ }
+ else
+ {
+ DBG("Promise %p has no future", p);
+ eina_value_flush(&value);
+ }
+ eina_mempool_free(_promise_mp, p);
+}
+
+Eina_Bool
+eina_promise_init(void)
+{
+ const char *choice = getenv("EINA_MEMPOOL");
+ if ((!choice) || (!choice[0])) choice = "chained_mempool";
+
+ RACE_STRUCT_MEMBERS[0].type = EINA_VALUE_TYPE_VALUE;
+ RACE_STRUCT_MEMBERS[1].type = EINA_VALUE_TYPE_UINT;
+
+ _promise_log_dom = eina_log_domain_register("eina_promise", EINA_COLOR_CYAN);
+ if (_promise_log_dom < 0)
+ {
+ EINA_LOG_ERR("Could not creae the Eina_Promise domain");
+ return EINA_FALSE;
+ }
+
+ //FIXME: Is 512 too high?
+ _promise_mp = eina_mempool_add(choice, "Eina_Promise",
+ NULL, sizeof(Eina_Promise), 512);
+ EINA_SAFETY_ON_NULL_GOTO(_promise_mp, err_promise);
+
+ _future_mp = eina_mempool_add(choice, "Eina_Future",
+ NULL, sizeof(Eina_Future), 512);
+ EINA_SAFETY_ON_NULL_GOTO(_future_mp, err_future);
+
+ return EINA_TRUE;
+
+ err_future:
+ eina_mempool_del(_promise_mp);
+ _promise_mp = NULL;
+ err_promise:
+ eina_log_domain_unregister(_promise_log_dom);
+ _promise_log_dom = -1;
+ return EINA_FALSE;
+}
+
+Eina_Bool
+eina_promise_shutdown(void)
+{
+ while (_pending_futures) _eina_future_cancel(_pending_futures->data, ECANCELED);
+ eina_mempool_del(_future_mp);
+ eina_mempool_del(_promise_mp);
+ eina_log_domain_unregister(_promise_log_dom);
+ _promise_log_dom = -1;
+ _promise_mp = NULL;
+ _future_mp = NULL;
+ return EINA_TRUE;
+}
+
+EAPI Eina_Value
+eina_promise_as_value(Eina_Promise *p)
+{
+ Eina_Value v = EINA_VALUE_EMPTY;
+ Eina_Bool r;
+ EINA_PROMISE_CHECK_RETURN_VAL(p, v);
+ r = eina_value_setup(&v, &EINA_VALUE_TYPE_PROMISE);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_setup);
+ r = eina_value_pset(&v, &p);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_pset);
+ DBG("Created value from promise %p", p);
+ return v;
+
+ err_pset:
+ eina_value_flush(&v);
+ memset(&v, 0, sizeof(Eina_Value));
+ err_setup:
+ if (p->future) _eina_future_cancel(p->future, ENOMEM);
+ else _eina_promise_cancel(p);
+ return v;
+}
+
+static void
+_eina_promise_clean_dispatch(Eina_Promise *p, Eina_Value v)
+{
+ Eina_Future *f = p->future;
+
+ if (f)
+ {
+ _eina_promise_value_dbg("Clean contenxt - Resolving promise", p, v);
+ _eina_promise_unlink(p);
+ _eina_future_dispatch(f, v);
+ }
+ eina_mempool_free(_promise_mp, p);
+}
+
+static Eina_Value
+_future_proxy(void *data, const Eina_Value v,
+ const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Value copy;
+ //We're in a safe context (from mainloop), so we can avoid scheduling a new dispatch
+ if (!v.type) copy = v;
+ else if (!eina_value_copy(&v, &copy))
+ {
+ ERR("Value cannot be copied - unusable with Eina_Future: %p (%s)", v.type, v.type->name);
+ eina_value_setup(&copy, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&copy, ENOTSUP);
+ }
+ _eina_promise_clean_dispatch(data, copy);
+ return v;
+}
+
+static void
+_proxy_cancel(void *data EINA_UNUSED, const Eina_Promise *dead_ptr EINA_UNUSED)
+{
+}
+
+static Eina_Future_Scheduler *
+_scheduler_get(Eina_Future *f)
+{
+ for (; f->prev != NULL; f = f->prev);
+ assert(f->promise != NULL);
+ return f->promise->scheduler;
+}
+
+EAPI Eina_Value
+eina_future_as_value(Eina_Future *f)
+{
+ Eina_Value v = EINA_VALUE_EMPTY;
+ Eina_Promise *p;
+ Eina_Future *r_future;
+
+ EINA_FUTURE_CHECK_RETURN_VAL(f, v);
+ p = eina_promise_new(_scheduler_get(f), _proxy_cancel, NULL);
+ EINA_SAFETY_ON_NULL_GOTO(p, err_promise);
+ r_future = eina_future_then(f, _future_proxy, p);
+ //If eina_future_then() fails f will be cancelled
+ EINA_SAFETY_ON_NULL_GOTO(r_future, err_future);
+
+ v = eina_promise_as_value(p);
+ if (v.type == &EINA_VALUE_TYPE_PROMISE)
+ {
+ DBG("Creating future proxy for future: %p - promise %p", f, p);
+ return v;
+ }
+
+ //The promise was freed by eina_promise_as_value()
+ ERR("Could not create a Eina_Value for future %p", f);
+ //Futures will be unlinked
+ _eina_future_free(r_future);
+ _eina_future_cancel(f, ENOMEM);
+ return v;
+
+ err_future:
+ /*
+ Do not cancel the _eina_future_cancel(f,..) here, it
+ was already canceled by eina_future_then().
+ */
+ _eina_promise_cancel(p);
+ return v;
+
+ err_promise:
+ _eina_future_cancel(f, ENOMEM);
+ return v;
+}
+
+EAPI Eina_Promise *
+eina_promise_new(Eina_Future_Scheduler *scheduler,
+ Eina_Promise_Cancel_Cb cancel_cb, const void *data)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cancel_cb, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(scheduler, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(scheduler->schedule, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(scheduler->recall, NULL);
+
+ Eina_Promise *p = eina_mempool_calloc(_promise_mp, sizeof(Eina_Promise));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
+ p->cancel = cancel_cb;
+ p->data = data;
+ p->scheduler = scheduler;
+ DBG("Creating new promise - Promise:%p, cb: %p, data:%p", p,
+ p->cancel, p->data);
+ return p;
+}
+
+EAPI void
+eina_future_cancel(Eina_Future *f)
+{
+ EINA_FUTURE_CHECK_RETURN(f);
+ _eina_future_cancel(f, ECANCELED);
+}
+
+EAPI void
+eina_promise_resolve(Eina_Promise *p, Eina_Value value)
+{
+ EINA_PROMISE_CHECK_GOTO(p, err);
+ _eina_promise_value_dbg("Resolve promise", p, value);
+ _eina_promise_deliver(p, value);
+ return;
+ err:
+ eina_value_flush(&value);
+}
+
+EAPI void
+eina_promise_reject(Eina_Promise *p, Eina_Error err)
+{
+ Eina_Value value;
+ Eina_Bool r;
+
+ EINA_PROMISE_CHECK_RETURN(p);
+ r = eina_value_setup(&value, EINA_VALUE_TYPE_ERROR);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_setup);
+ r = eina_value_set(&value, err);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_set);
+ DBG("Reject promise %p - Error msg: '%s' - Error code: %d", p,
+ eina_error_msg_get(err), err);
+ _eina_promise_deliver(p, value);
+ return;
+
+ err_set:
+ eina_value_flush(&value);
+ err_setup:
+ if (p->future) _eina_future_cancel(p->future, ENOMEM);
+ else _eina_promise_cancel(p);
+}
+
+static void
+_fake_future_dispatch(const Eina_Future_Desc desc, int err)
+{
+ /*
+ This function is used to dispatch the Eina_Future_Cb in case,
+ the future creation fails. By calling the Eina_Future_Cb
+ the user has a chance to free allocated resources.
+ */
+ Eina_Value v, r;
+
+ if (desc.storage) *desc.storage = NULL;
+ if (!desc.cb) return;
+ eina_value_setup(&v, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&v, err);
+ //Since the future was not created the dead_ptr is NULL.
+ r = desc.cb((void *)desc.data, v, NULL);
+ eina_value_flush(&r);
+ eina_value_flush(&v);
+}
+
+static Eina_Future *
+_eina_future_new(Eina_Promise *p, const Eina_Future_Desc desc)
+{
+ Eina_Future *f;
+
+ f = eina_mempool_calloc(_future_mp, sizeof(Eina_Future));
+ EINA_SAFETY_ON_NULL_GOTO(f, err_future);
+ _eina_promise_link(p, f);
+ f->cb = desc.cb;
+ f->data = desc.data;
+ if (desc.storage)
+ {
+ *desc.storage = f;
+ f->storage = desc.storage;
+ }
+ DBG("Creating new future - Promise:%p, Future:%p, cb: %p, data: %p ",
+ p, f, f->cb, f->data);
+ return f;
+
+ err_future:
+ _fake_future_dispatch(desc, ENOMEM);
+ if (p) _eina_promise_cancel(p);
+ return NULL;
+}
+
+EAPI Eina_Future *
+eina_future_new(Eina_Promise *p)
+{
+ static const Eina_Future_Desc desc = {
+ .cb = NULL,
+ .data = NULL
+ };
+
+ EINA_PROMISE_CHECK_RETURN_VAL(p, NULL);
+ EINA_SAFETY_ON_TRUE_GOTO(p->future != NULL, err_has_future);
+
+ return _eina_future_new(p, desc);
+
+ err_has_future:
+ //_eina_future_cancel() will also cancel the promise
+ _eina_future_cancel(p->future, EINVAL);
+ return NULL;
+}
+
+static Eina_Future *
+_eina_future_then(Eina_Future *prev, const Eina_Future_Desc desc)
+{
+ Eina_Future *next = _eina_future_new(NULL, desc);
+ EINA_SAFETY_ON_NULL_GOTO(next, err_next);
+ next->prev = prev;
+ prev->next = next;
+ DBG("Linking futures - Prev:%p Next:%p", prev, next);
+ return next;
+
+ err_next:
+ //_fake_future_dispatch() already called by _eina_future_new()
+ _eina_future_cancel(prev, ENOMEM);
+ return NULL;
+}
+
+EAPI Eina_Future *
+eina_future_then_from_desc(Eina_Future *prev, const Eina_Future_Desc desc)
+{
+ EINA_FUTURE_CHECK_GOTO(prev, err_future);
+ EINA_SAFETY_ON_TRUE_GOTO(prev->next != NULL, err_next);
+ return _eina_future_then(prev, desc);
+
+ err_next:
+ _eina_future_cancel(prev->next, EINVAL);
+ err_future:
+ _fake_future_dispatch(desc, EINVAL);
+ return NULL;
+}
+
+EAPI Eina_Future *
+eina_future_chain_array(Eina_Future *prev, const Eina_Future_Desc descs[])
+{
+ Eina_Future *f = prev;
+ ssize_t i = -1;
+ int err = EINVAL;
+
+ EINA_FUTURE_CHECK_GOTO(prev, err_prev);
+ EINA_SAFETY_ON_TRUE_GOTO(prev->next != NULL, err_next);
+
+ err = ENOMEM;
+ for (i = 0; descs[i].cb; i++)
+ {
+ f = _eina_future_then(f, descs[i]);
+ /*
+ If _eina_future_then() fails the whole chain will be cancelled by it.
+ All we need to do is free the remaining descs..
+ */
+ EINA_SAFETY_ON_NULL_GOTO(f, err_prev);
+ }
+
+ return f;
+
+ err_next:
+ _eina_future_cancel(f, err);
+ err_prev:
+ /*
+ If i > 0 we'll start to dispatch fake futures
+ at i + 1, since the descs[i] was already freed
+ by _eina_future_then()
+ */
+ for (i = i + 1; descs[i].cb; i++)
+ _fake_future_dispatch(descs[i], err);
+ return NULL;
+}
+
+EAPI Eina_Future *
+eina_future_chain_easy_array(Eina_Future *prev, const Eina_Future_Cb_Easy_Desc descs[])
+{
+ size_t i = -1;
+ Eina_Future *f = prev;
+ int err = EINVAL;
+
+ EINA_FUTURE_CHECK_GOTO(prev, err_prev);
+ EINA_SAFETY_ON_TRUE_GOTO(prev->next != NULL, err_next);
+
+ err = ENOMEM;
+ for (i = 0; descs[i].success || descs[i].error || descs[i].free || descs[i].success_type; i++)
+ {
+ Eina_Future_Desc fdesc = eina_future_cb_easy_from_desc(descs[i]);
+ f = _eina_future_then(f, fdesc);
+ EINA_SAFETY_ON_NULL_GOTO(f, err_prev);
+ }
+
+ return f;
+
+ err_next:
+ _eina_future_cancel(f, err);
+ err_prev:
+ /*
+ If i > 0 we'll start to dispatch fake futures
+ at i + 1, since the descs[i] was already freed
+ by _eina_future_then()
+ */
+ for (i = i + 1; descs[i].error || descs[i].free; i++)
+ {
+ if (descs[i].error)
+ {
+ Eina_Value v = descs[i].error((void *)descs[i].data, err);
+ eina_value_flush(&v);
+ }
+ if (descs[i].free) descs[i].free((void *)descs[i].data, NULL);
+ }
+ return NULL;
+}
+
+static Eina_Value
+_eina_future_cb_console(void *data,
+ const Eina_Value value,
+ const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Future_Cb_Console_Desc *c = data;
+ const char *prefix = c ? c->prefix : NULL;
+ const char *suffix = c ? c->suffix : NULL;
+ const char *content = "no value";
+ char *str = NULL;
+
+ if (value.type)
+ {
+ str = eina_value_to_string(&value);
+ content = str;
+ }
+
+ if (!prefix) prefix = "";
+ if (!suffix) suffix = "\n";
+ printf("%s%s%s", prefix, content, suffix);
+ free(str);
+ if (c) {
+ free((void *)c->prefix);
+ free((void *)c->suffix);
+ free(c);
+ }
+ return value;
+}
+
+EAPI Eina_Future_Desc
+eina_future_cb_console_from_desc(const Eina_Future_Cb_Console_Desc desc)
+{
+ Eina_Future_Cb_Console_Desc *c;
+ Eina_Future_Desc fdesc = {
+ .cb = _eina_future_cb_console,
+ .data = NULL
+ };
+
+ if (desc.prefix || desc.suffix) {
+ fdesc.data = c = calloc(1, sizeof(Eina_Future_Cb_Console_Desc));
+ EINA_SAFETY_ON_NULL_GOTO(c, exit);
+ c->prefix = desc.prefix ? strdup(desc.prefix) : NULL;
+ c->suffix = desc.suffix ? strdup(desc.suffix) : NULL;
+ }
+ exit:
+ return fdesc;
+}
+
+static Eina_Value
+_eina_future_cb_convert_to(void *data, const Eina_Value src,
+ const Eina_Future *dead_future EINA_UNUSED)
+{
+ const Eina_Value_Type *type = data;
+ Eina_Value dst = EINA_VALUE_EMPTY;
+
+ if (!type)
+ {
+ DBG("Trying to convert type '%s' to a NULL type - Ignoring (pass thru)",
+ src.type->name);
+ return src; // pass thru
+ }
+
+ if (!eina_value_setup(&dst, type))
+ {
+ ERR("Could not setup an Eina_Value with type named: '%s'", type->name);
+ eina_value_setup(&dst, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&dst, ENOMEM);
+ }
+ else if (src.type && !eina_value_convert(&src, &dst))
+ {
+ //Clean the type
+ ERR("Could not convert the Eina_Value type %s to %s",
+ src.type->name, dst.type->name);
+ eina_value_flush(&dst);
+ eina_value_setup(&dst, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&dst, ENOTSUP);
+ }// otherwise leave initial value for empty
+
+ return dst;
+}
+
+EAPI Eina_Future_Desc
+eina_future_cb_convert_to(const Eina_Value_Type *type)
+{
+ return (Eina_Future_Desc){.cb = _eina_future_cb_convert_to, .data = type};
+}
+
+EAPI void *
+eina_promise_data_get(const Eina_Promise *p)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
+ return (void *)p->data;
+}
+
+static Eina_Value
+_eina_future_cb_easy(void *data, const Eina_Value value,
+ const Eina_Future *dead_future)
+{
+ Eina_Future_Cb_Easy_Desc *d = data;
+ Eina_Value ret = EINA_VALUE_EMPTY;
+ if (!d)
+ {
+ if (eina_value_setup(&ret, EINA_VALUE_TYPE_ERROR)) eina_value_set(&ret, ENOMEM);
+ return ret;
+ }
+ EASY_FUTURE_DISPATCH(ret, value, dead_future, d, (void*)d->data);
+ free(d);
+ return ret;
+}
+
+EAPI Eina_Future_Desc
+eina_future_cb_easy_from_desc(const Eina_Future_Cb_Easy_Desc desc)
+{
+ Eina_Future_Cb_Easy_Desc *d = calloc(1, sizeof(Eina_Future_Cb_Easy_Desc));
+ EINA_SAFETY_ON_NULL_GOTO(d, end);
+ *d = desc;
+ end:
+ return (Eina_Future_Desc){ .cb = _eina_future_cb_easy, .data = d };
+}
+
+typedef struct _Base_Ctx {
+ Eina_Promise *promise;
+ Eina_Future **futures;
+ unsigned int futures_len;
+} Base_Ctx;
+
+typedef struct _All_Promise_Ctx {
+ Base_Ctx base;
+ Eina_Value values;
+ unsigned int processed;
+} All_Promise_Ctx;
+
+typedef struct _Race_Promise_Ctx {
+ Base_Ctx base;
+ Eina_Bool dispatching;
+} Race_Promise_Ctx;
+
+static void
+_base_ctx_clean(Base_Ctx *ctx)
+{
+ unsigned int i;
+ for (i = 0; i < ctx->futures_len; i++)
+ if (ctx->futures[i]) _eina_future_cancel(ctx->futures[i], ECANCELED);
+ free(ctx->futures);
+}
+
+static void
+_all_promise_ctx_free(All_Promise_Ctx *ctx)
+{
+ _base_ctx_clean(&ctx->base);
+ eina_value_flush(&ctx->values);
+ free(ctx);
+}
+
+static void
+_all_promise_cancel(void *data, const Eina_Promise *dead EINA_UNUSED)
+{
+ _all_promise_ctx_free(data);
+}
+
+static void
+_race_promise_ctx_free(Race_Promise_Ctx *ctx)
+{
+ _base_ctx_clean(&ctx->base);
+ free(ctx);
+}
+
+static void
+_race_promise_cancel(void *data, const Eina_Promise *dead EINA_UNUSED)
+{
+ _race_promise_ctx_free(data);
+}
+
+static Eina_Bool
+_future_unset(Base_Ctx *ctx, unsigned int *pos, const Eina_Future *dead_ptr)
+{
+ unsigned int i;
+
+ for (i = 0; i < ctx->futures_len; i++)
+ {
+ if (ctx->futures[i] == dead_ptr)
+ {
+ ctx->futures[i] = NULL;
+ *pos = i;
+ return EINA_TRUE;
+ }
+ }
+ return EINA_FALSE;
+}
+
+static Eina_Value
+_race_then_cb(void *data, const Eina_Value v,
+ const Eina_Future *dead_ptr)
+{
+ Race_Promise_Ctx *ctx = data;
+ Eina_Promise *p = ctx->base.promise;
+ Eina_Bool found, r;
+ Eina_Value result;
+ unsigned int i;
+
+ //This is not allowed!
+ assert(v.type != &EINA_VALUE_TYPE_PROMISE);
+ found = _future_unset(&ctx->base, &i, dead_ptr);
+ assert(found);
+
+ if (ctx->dispatching) return EINA_VALUE_EMPTY;
+ ctx->dispatching = EINA_TRUE;
+
+ //By freeing the race_ctx all the other futures will be cancelled.
+ _race_promise_ctx_free(ctx);
+
+ r = eina_value_struct_setup(&result, &RACE_STRUCT_DESC);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_setup);
+ r = eina_value_struct_set(&result, "value", v);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_set);
+ r = eina_value_struct_set(&result, "index", i);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_set);
+ //We're in a safe context (from mainloop), so we can avoid scheduling a new dispatch
+ _eina_promise_clean_dispatch(p, result);
+ return v;
+
+ err_set:
+ eina_value_flush(&result);
+ err_setup:
+ eina_value_setup(&result, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&result, ENOMEM);
+ _eina_promise_clean_dispatch(p, result);
+ return v;
+}
+
+static Eina_Value
+_all_then_cb(void *data, const Eina_Value v,
+ const Eina_Future *dead_ptr)
+{
+ All_Promise_Ctx *ctx = data;
+ unsigned int i = 0;
+ Eina_Bool found;
+
+ //This is not allowed!
+ assert(v.type != &EINA_VALUE_TYPE_PROMISE);
+
+ found = _future_unset(&ctx->base, &i, dead_ptr);
+ assert(found);
+
+ ctx->processed++;
+ eina_value_array_set(&ctx->values, i, v);
+ if (ctx->processed == ctx->base.futures_len)
+ {
+ //We're in a safe context (from mainloop), so we can avoid scheduling a new dispatch
+ _eina_promise_clean_dispatch(ctx->base.promise, ctx->values);
+ ctx->values = EINA_VALUE_EMPTY; /* flushed in _eina_promise_clean_dispatch() */
+ _all_promise_ctx_free(ctx);
+ }
+ return v;
+}
+
+static void
+_future_array_cancel(Eina_Future *array[])
+{
+ size_t i;
+ for (i = 0; array[i]; i++) _eina_future_cancel(array[i], ENOMEM);
+}
+
+static Eina_Bool
+promise_proxy_of_future_array_create(Eina_Future *array[],
+ Base_Ctx *ctx,
+ Eina_Promise_Cancel_Cb cancel_cb,
+ Eina_Future_Cb future_cb)
+{
+ unsigned int i;
+
+ //Count how many futures...
+ for (i = 0; array[i] != EINA_FUTURE_SENTINEL; i++)
+ //NULL futures are not allowed.
+ EINA_SAFETY_ON_NULL_RETURN_VAL(array[i], EINA_FALSE);
+
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(i > 0, EINA_FALSE);
+
+ ctx->promise = eina_promise_new(_scheduler_get(array[0]), cancel_cb, ctx);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ctx->promise, EINA_FALSE);
+
+ ctx->futures_len = i;
+ ctx->futures = calloc(ctx->futures_len, sizeof(Eina_Future *));
+ EINA_SAFETY_ON_NULL_GOTO(ctx->futures, err_futures);
+
+ for (i = 0; i < ctx->futures_len; i++)
+ {
+ ctx->futures[i] = eina_future_then(array[i], future_cb, ctx);
+ //Futures will be cancelled by the caller...
+ EINA_SAFETY_ON_NULL_GOTO(ctx->futures[i], err_then);
+ }
+ return EINA_TRUE;
+
+ err_then:
+ //This will also unlink the prev future...
+ while (i >= 1) _eina_future_free(ctx->futures[--i]);
+ free(ctx->futures);
+ ctx->futures = NULL;
+ err_futures:
+ ctx->futures_len = 0;
+ eina_mempool_free(_promise_mp, ctx->promise);
+ ctx->promise = NULL;
+ return EINA_FALSE;
+}
+
+EAPI Eina_Promise *
+eina_promise_all_array(Eina_Future *array[])
+{
+ All_Promise_Ctx *ctx;
+ unsigned int i;
+ Eina_Bool r;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL);
+ ctx = calloc(1, sizeof(All_Promise_Ctx));
+ EINA_SAFETY_ON_NULL_GOTO(ctx, err_ctx);
+ r = eina_value_array_setup(&ctx->values, EINA_VALUE_TYPE_VALUE, 0);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_array);
+ r = promise_proxy_of_future_array_create(array, &ctx->base,
+ _all_promise_cancel, _all_then_cb);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_promise);
+
+ for (i = 0; i < ctx->base.futures_len; i++)
+ {
+ Eina_Bool r;
+ Eina_Value v;
+
+ //Stub values...
+ r = eina_value_setup(&v, EINA_VALUE_TYPE_INT);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_stub);
+ r = eina_value_array_append(&ctx->values, v);
+ eina_value_flush(&v);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_stub);
+ }
+
+ return ctx->base.promise;
+
+ err_stub:
+ for (i = 0; i < ctx->base.futures_len; i++) _eina_future_free(ctx->base.futures[i]);
+ free(ctx->base.futures);
+ eina_mempool_free(_promise_mp, ctx->base.promise);
+ err_promise:
+ eina_value_flush(&ctx->values);
+ err_array:
+ free(ctx);
+ err_ctx:
+ _future_array_cancel(array);
+ return NULL;
+}
+
+EAPI Eina_Promise *
+eina_promise_race_array(Eina_Future *array[])
+{
+ Race_Promise_Ctx *ctx;
+ Eina_Bool r;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(array, NULL);
+ ctx = calloc(1, sizeof(Race_Promise_Ctx));
+ EINA_SAFETY_ON_NULL_GOTO(ctx, err_ctx);
+ r = promise_proxy_of_future_array_create(array, &ctx->base,
+ _race_promise_cancel,
+ _race_then_cb);
+ EINA_SAFETY_ON_FALSE_GOTO(r, err_promise);
+
+ return ctx->base.promise;
+
+ err_promise:
+ free(ctx);
+ err_ctx:
+ _future_array_cancel(array);
+ return NULL;
+}
+
+static Eina_Value
+_eina_future_cb_ignore_error(void *data, const Eina_Value value,
+ const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Error expected_err = (Eina_Error)(long)data;
+
+ if (value.type == EINA_VALUE_TYPE_ERROR)
+ {
+ Eina_Error err;
+ eina_value_get(&value, &err);
+ if ((!expected_err) || (expected_err == err))
+ {
+ DBG("ignored error %d (%s)", err, eina_error_msg_get(err));
+ return EINA_VALUE_EMPTY;
+ }
+ }
+ return value;
+}
+
+EAPI Eina_Future_Desc
+eina_future_cb_ignore_error(Eina_Error err)
+{
+ return (Eina_Future_Desc){ _eina_future_cb_ignore_error, (void *)(long)err };
+}
+
+EAPI void
+eina_future_desc_flush(Eina_Future_Desc *desc)
+{
+ if (!desc) return;
+ _fake_future_dispatch(*desc, ECANCELED);
+ memset(desc, 0, sizeof(Eina_Future_Desc));
+}
+
+EAPI void
+eina_future_cb_easy_desc_flush(Eina_Future_Cb_Easy_Desc *desc)
+{
+ if (!desc) return;
+
+ if (desc->error)
+ {
+ Eina_Value r;
+
+ r = desc->error((void *)desc->data, ECANCELED);
+ eina_value_flush(&r);
+ }
+
+ if (desc->free) desc->free((void *)desc->data, NULL);
+ memset(desc, 0, sizeof(Eina_Future_Cb_Easy_Desc));
+}
+
+static Eina_Value
+_future_cb_log(void *data, const Eina_Value value,
+ const Eina_Future *dead EINA_UNUSED)
+{
+ Eina_Future_Cb_Log_Desc *ctx = data;
+ const char *content = "no value";
+ char *str = NULL;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, value);
+
+ if (value.type)
+ {
+ str = eina_value_to_string(&value);
+ content = str;
+ }
+
+ eina_log_print(ctx->domain, ctx->level,
+ ctx->file ? ctx->file : "Unknown file",
+ ctx->func ? ctx->func : "Unknown function",
+ ctx->line, "%s%s%s",
+ ctx->prefix ? ctx->prefix : "",
+ content,
+ ctx->suffix ? ctx->suffix : "");
+ free(str);
+ free((void *)ctx->func);
+ free((void *)ctx->file);
+ free((void *)ctx->prefix);
+ free((void *)ctx->suffix);
+ free(ctx);
+ return value;
+}
+
+EAPI Eina_Future_Desc
+eina_future_cb_log_from_desc(const Eina_Future_Cb_Log_Desc desc)
+{
+ Eina_Future_Cb_Log_Desc *ctx = calloc(1, sizeof(Eina_Future_Cb_Log_Desc));
+ EINA_SAFETY_ON_NULL_GOTO(ctx, exit);
+
+ if (desc.prefix) ctx->prefix = strdup(desc.prefix);
+ if (desc.suffix) ctx->suffix = strdup(desc.suffix);
+ if (desc.file) ctx->file = strdup(desc.file);
+ if (desc.func) ctx->func = strdup(desc.func);
+ ctx->domain = desc.domain;
+ ctx->level = desc.level;
+ ctx->line = desc.line;
+
+ exit:
+ return (Eina_Future_Desc){ .cb = _future_cb_log, .data = ctx };
+}
diff --git a/src/lib/eina/eina_promise.h b/src/lib/eina/eina_promise.h
new file mode 100644
index 0000000000..43aa90ad16
--- /dev/null
+++ b/src/lib/eina/eina_promise.h
@@ -0,0 +1,1674 @@
+#ifndef _EINA_PROMISE_H_
+#define _EINA_PROMISE_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "eina_safety_checks.h"
+#include "eina_types.h"
+#include "eina_value.h"
+
+/**
+ * @ingroup Eina_Promise
+ *
+ * @{
+ */
+typedef struct _Eina_Future_Desc Eina_Future_Desc;
+typedef struct _Eina_Promise Eina_Promise;
+typedef struct _Eina_Future Eina_Future;
+typedef struct _Eina_Future_Cb_Easy_Desc Eina_Future_Cb_Easy_Desc;
+typedef struct _Eina_Future_Cb_Console_Desc Eina_Future_Cb_Console_Desc;
+typedef struct _Eina_Future_Scheduler Eina_Future_Scheduler;
+typedef struct _Eina_Future_Schedule_Entry Eina_Future_Schedule_Entry;
+typedef struct _Eina_Future_Race_Result Eina_Future_Race_Result;
+typedef struct _Eina_Future_Cb_Log_Desc Eina_Future_Cb_Log_Desc;
+
+/**
+ * @defgroup Eina_Future_Callbacks Efl Future Callbacks
+ * @ingroup eina_future
+ * @typedef Eina_Future_Cb Eina_Future_Cb
+ *
+ * A callback used to inform that a future was resolved.
+ * Usually this callback is called from a clean context, that is, from the
+ * main loop or some platform defined safe context. However there are
+ * 2 exceptions:
+ *
+ * @li eina_future_cancel() was used, it's called immediately in the
+ * context that called cancel using `ECANCELED` as error.
+ *
+ * @li eina_future_then(), eina_future_then_from_desc(), eina_future_chain(), eina_future_chain_array()
+ * or similar failed due invalid pointer or memory allocation. Then the callback is called from the
+ * failed context using `EINVAL` or `ENOMEM` as errors and @p dead_future will be @c NULL.
+ *
+ * @param data The data provided by the user
+ *
+ * @param value An Eina_Value which contains the operation result. Before using
+ * the @p value, its type must be checked in order to avoid errors. This is needed, because
+ * if an operation fails the Eina_Value type will be EINA_VALUE_TYPE_ERROR
+ * which is a different type than the expected operation result.
+ *
+ * @param dead_future A pointer to the future that was completed.
+ *
+ * @return An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ * If there is no need to convert the received value, it's @b recommended
+ * to pass-thru @p value argument. If you need to convert to a different type
+ * or generate a new value, use @c eina_value_setup() on @b another Eina_Value
+ * and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ * whole chain will wait until the promise is resolved in
+ * order to continue its execution.
+ * Note that the value contents must survive this function scope,
+ * that is, do @b not use stack allocated blobs, arrays, structures or types that
+ * keeps references to memory you give. Values will be automatically cleaned up
+ * using @c eina_value_flush() once they are unused (no more future or futures
+ * returned a new value).
+ *
+ * @note The returned value @b can be an EFL_VALUE_TYPE_PROMISE! (see eina_promise_as_value() and
+ * eina_future_as_value()) In this case the future chain will wait until the promise is resolved.
+ *
+ * @see eina_future_cancel()
+ * @see eina_future_then()
+ * @see eina_future_then_from_desc()
+ * @see eina_future_then_easy()
+ * @see eina_future_chain()
+ * @see eina_future_chain_array()
+ * @see eina_future_as_value()
+ * @see eina_promise_as_value()
+ * @{
+ */
+typedef Eina_Value (*Eina_Future_Cb)(void *data, const Eina_Value value, const Eina_Future *dead_future);
+
+/**
+ * @struct _Eina_Future_Scheduler
+ * @ingroup eina_promise
+ *
+ * A struct that represents an scheduled event.
+ * This struct may be used by Eina to cancel
+ * a scheduled future.
+ *
+ * @see eina_promise_new()
+ *
+ * @see #Eina_Future_Scheduler
+ * @see #Eina_Future_Scheduler_Cb
+ */
+struct _Eina_Future_Schedule_Entry {
+ /**
+ * The scheduler used to create the entry.
+ * @note This must not be @c NULL.
+ */
+ Eina_Future_Scheduler *scheduler;
+};
+
+
+/**
+ * @typedef Eina_Future_Scheduler_Cb
+ * @ingroup eina_promise
+ * A callback used by the Eina_Future_Scheduler to deliver
+ * the future operation result.
+ *
+ * @param f The delivered future.
+ * @param value The future result
+ *
+ *
+ * @see eina_promise_new()
+ *
+ * @see #Eina_Future_Schedule_Entry
+ * @see #Eina_Future_Scheduler
+ */
+typedef void (*Eina_Future_Scheduler_Cb)(Eina_Future *f, Eina_Value value);
+
+/**
+ * @struct _Eina_Future_Scheduler
+ * @ingroup eina_promise
+ * This struct is used as a bridge between Eina and the future scheduler.
+ * By using the functions provided by #_Eina_Future_Scheduler Eina can
+ * schedule futures resolutions, rejections and cancelations to a safe context.
+ *
+ * @see eina_promise_new()
+ * @see #Eina_Future_Schedule_Entry
+ * @see #Eina_Future_Scheduler_Cb
+ */
+struct _Eina_Future_Scheduler {
+ /**
+ * Called by @p Eina_Future when a delivery must be scheduled to a safe context.
+ * ie: after @p eina_promise_resolve()
+ *
+ * @note Must not be @c NULL
+ *
+ * Must call back from a safe context using @p cb(f,value)
+ * @param scheduler The scheduler to use.
+ * @param cb The #Eina_Future_Scheduler_Cb to be called and deliver the @p f and @p value.
+ * @param f The future to be delivered to @p cb
+ * @param value The value to be delivered to @p cb
+ * @return A scheduled entry or @c NULL on error
+ */
+ Eina_Future_Schedule_Entry *(*schedule)(Eina_Future_Scheduler *scheduler, Eina_Future_Scheduler_Cb cb, Eina_Future *f, Eina_Value value);
+ /**
+ * Called by @p Eina_Future when a delivery must be cancelled.
+ * ie: after @p eina_future_cancel()
+ *
+ * @note Must not be @c NULL.
+ *
+ * @param entry The scheduled event to cancel
+ */
+ void (*recall)(Eina_Future_Schedule_Entry *entry);
+};
+
+/**
+ * @typedef Eina_Promise_Cancel_Cb Eina_Promise_Cancel_Cb.
+ * @ingroup eina_promise
+ *
+ * A callback used to inform that a promise was canceled.
+ *
+ * A promise may be canceled by the user calling `eina_future_cancel()`
+ * on any Eina_Future that is part of the chain that uses an Eina_Promise,
+ * that will cancel the whole chain and then the promise.
+ *
+ * It should stop all asynchronous operations or at least mark them to be
+ * discarded instead of resolved. Actually it can't be resolved once
+ * cancelled since the given pointer @c dead_promise is now invalid.
+ *
+ * @note This callback is @b mandatory for a reason, do not provide an empty
+ * callback as it'll likely result in memory corruption and invalid access.
+ * If impossible to cancel an asynchronous task, then create an
+ * intermediate memory to hold Eina_Promise and make it @c NULL,
+ * in this callback. Then prior to resolution check if the pointer is set.
+ *
+ * @note This callback is @b not called if eina_promise_resolve() or
+ * eina_promise_reject() are used. If any cleanup is needed, then
+ * call yourself. It's only meant as cancellation, not a general
+ * "free callback".
+ *
+ * @param data The data provided by the user.
+ * @param dead_promise The canceled promise.
+ * @see eina_promise_reject()
+ * @see eina_promise_resolve()
+ * @see eina_future_cancel()
+ */
+typedef void (*Eina_Promise_Cancel_Cb) (void *data, const Eina_Promise *dead_promise);
+
+/**
+ * @typedef Eina_Future_Success_Cb Eina_Future_Success_Cb.
+ * @ingroup eina_future
+ *
+ * A callback used to inform that the future completed with success.
+ *
+ * Unlike #Eina_Future_Cb this callback only called if the future operation was successful, this is,
+ * no errors occurred (@p value type differs from EINA_VALUE_TYPE_ERROR)
+ * and the @p value matches #_Eina_Future_Cb_Easy_Desc::success_type (if given).
+ * In case #_Eina_Future_Cb_Easy_Desc::success_type was not supplied (it's @c NULL) the @p value type
+ * must be checked before using it.
+ *
+ * @note This function is always called from a safe context (main loop or some platform defined safe context).
+ *
+ * @param data The data provided by the user.
+ * @param value The operation result
+ * @return An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ * If there is no need to convert the received value, it's @b recommended
+ * to pass-thru @p value argument. If you need to convert to a different type
+ * or generate a new value, use @c eina_value_setup() on @b another Eina_Value
+ * and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ * whole chain will wait until the promise is resolved in
+ * order to continue its execution.
+ * Note that the value contents must survive this function scope,
+ * that is, do @b not use stack allocated blobs, arrays, structures or types that
+ * keeps references to memory you give. Values will be automatically cleaned up
+ * using @c eina_value_flush() once they are unused (no more future or futures
+ * returned a new value).
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ */
+typedef Eina_Value (*Eina_Future_Success_Cb)(void *data, const Eina_Value value);
+
+/**
+ * @typedef Eina_Future_Error_Cb Eina_Future_Error_Cb.
+ * @ingroup eina_future
+ *
+ * A callback used to inform that the future completed with failure.
+ *
+ * Unlike #Eina_Future_Success_Cb this function is only called when an error
+ * occurs during the future process or when #_Eina_Future_Cb_Easy_Desc::success_type
+ * differs from the future result.
+ * On future creation errors and future cancellation this function will be called
+ * from the current context with the following errors respectitally: `EINVAL`, `ENOMEM` and `ECANCELED`.
+ * Otherwise this function is called from a safe context.
+ *
+ * If it was possible to recover from an error this function should return an empty value
+ * `return EINA_VALUE_EMPTY;` or any other Eina_Value type that differs from EINA_VALUE_TYPE_ERROR.
+ * In this case the error will not be reported by the other futures in the chain (if any), otherwise
+ * if an Eina_Value type EINA_VALUE_TYPE_ERROR is returned the error will continue to be reported
+ * to the other futures in the chain.
+ *
+ * @param data The data provided by the user.
+ * @param error The operation error
+ * @return An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ * If you need to convert to a different type or generate a new value,
+ * use @c eina_value_setup() on @b another Eina_Value
+ * and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ * whole chain will wait until the promise is resolved in
+ * order to continue its execution.
+ * Note that the value contents must survive this function scope,
+ * that is, do @b not use stack allocated blobs, arrays, structures or types that
+ * keeps references to memory you give. Values will be automatically cleaned up
+ * using @c eina_value_flush() once they are unused (no more future or futures
+ * returned a new value).
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ */
+typedef Eina_Value (*Eina_Future_Error_Cb)(void *data, const Eina_Error error);
+
+/**
+ * @typedef Eina_Future_Free_Cb Eina_Future_Free_Cb.
+ * @ingroup eina_future
+ *
+ * A callback used to inform that the future was freed and the user should also @c free the @p data.
+ * This callback may be called from an unsafe context if the future was canceled or an error
+ * occurred.
+ *
+ * @note This callback is always called, even if #Eina_Future_Error_Cb and/or #Eina_Future_Success_Cb
+ * were not provided, which can also be used to monitor when a future ends.
+ *
+ * @param data The data provided by the user.
+ * @param dead_future The future that was freed.
+ *
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ */
+typedef void (*Eina_Future_Free_Cb)(void *data, const Eina_Future *dead_future);
+
+/**
+ * @struct _Eina_Future_Cb_Easy_Desc
+ * @ingroup eina_future
+ *
+ * A struct with callbacks to be used by eina_future_cb_easy_from_desc() and eina_future_cb_easy()
+ *
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ */
+struct _Eina_Future_Cb_Easy_Desc {
+ /**
+ * Called on success (value.type is not @c EINA_VALUE_TYPE_ERROR).
+ *
+ * if @c success_type is not NULL, then the value is guaranteed to be of that type,
+ * if it's not, then it will trigger @c error with @c EINVAL.
+ *
+ * After this function returns, @c free callback is called if provided.
+ */
+ Eina_Future_Success_Cb success;
+ /**
+ * Called on error (value.type is @c EINA_VALUE_TYPE_ERROR).
+ *
+ * This function can return another error, propagating or converting it. However it
+ * may also return a non-error, in this case the next future in chain will receive a regular
+ * value, which may call its @c success.
+ *
+ * If this function is not provided, then it will pass thru the error to the next error handler.
+ *
+ * It may be called with @c EINVAL if @c success_type is provided and doesn't
+ * match the received type.
+ *
+ * It may be called with @c ECANCELED if future was canceled.
+ *
+ * It may be called with @c ENOMEM if memory allocation failed during callback creation.
+ *
+ * After this function returns, @c free callback is called if provided.
+ */
+ Eina_Future_Error_Cb error;
+ /**
+ * Called on @b all situations to notify future destruction.
+ *
+ * This is called after @c success or @c error, as well as it's called if none of them are
+ * provided. Thus can be used as a "weak ref" mechanism.
+ */
+ Eina_Future_Free_Cb free;
+ /**
+ * If provided, then @c success will only be called if the value type matches the given pointer.
+ *
+ * If provided and doesn't match, then @c error will be called with @c EINVAL. If no @c error,
+ * then it will be propagated to the next future in the chain.
+ */
+ const Eina_Value_Type *success_type;
+ /**
+ * Context data given to every callback.
+ *
+ * This must be freed @b only by @c free callback as it's called from every case,
+ * otherwise it may lead to memory leaks.
+ */
+ const void *data;
+};
+
+/**
+ * @struct _Eina_Future_Cb_Console_Desc
+ * @ingroup eina_future
+ *
+ * A struct used to define the prefix and suffix to be printed
+ * along side the a future value. This struct is used by
+ * eina_future_cb_console_from_desc()
+ *
+ * @see eina_future_cb_console_from_desc()
+ */
+struct _Eina_Future_Cb_Console_Desc {
+ /**
+ * The prefix to be printed. If @c NULL an empty string ("") is used.
+ */
+ const char *prefix;
+ /**
+ * The suffix the be printed. If @c NULL '\n' is used.
+ */
+ const char *suffix;
+};
+
+/**
+ * @struct _Eina_Future_Cb_Log_Desc
+ * @ingroup eina_future
+ *
+ * This struct is used by eina_future_cb_log_from_desc() and
+ * its contents will be routed to eina_log_print() along side
+ * the future value.
+ *
+ * @see eina_future_cb_log_from_desc()
+ */
+struct _Eina_Future_Cb_Log_Desc {
+ /**
+ * The prefix to be printed. If @c NULL an empty string ("") is used.
+ */
+ const char *prefix;
+ /**
+ * The suffix the be printed. If @c NULL '\n' is used.
+ */
+ const char *suffix;
+ /**
+ * The file name to be passed to eina_log_print(). if @c NULL "Unknown file" is used.
+ */
+ const char *file;
+ /**
+ * The file name to be passed to eina_log_print(). if @c NULL "Unknown function" is used.
+ */
+ const char *func;
+ /**
+ * The Eina_Log_Level to use.
+ */
+ Eina_Log_Level level;
+ /**
+ * The log domain to use.
+ */
+ int domain;
+ /**
+ * The line number to be passed to eina_log_print().
+ */
+ int line;
+};
+
+/**
+ * @struct _Eina_Future_Desc
+ * @ingroup eina_future
+ * A struct used to define a callback and data for a future.
+ *
+ * This struct contains a future completion callback and a data to the future
+ * completion callback which is used by eina_future_then(), eina_future_chain()
+ * and friends to inform the user about the future result. The #_Eina_Future_Desc::data
+ * variable should be freed when #_Eina_Future_Desc::cb is called, otherwise it will leak.
+ *
+ * @note If eina_future_then(), eina_future_chain() and friends fails to return a valid future
+ * (in other words: @c NULL is returned) the #_Eina_Future_Desc::cb will be called
+ * report an error like `EINVAL` or `ENOMEM` so #_Eina_Future_Desc::data can be freed.
+ *
+ * @see eina_future_then()
+ * @see eina_future_chain_array()
+ * @see eina_future_cb_convert_to()
+ * @see eina_future_cb_console_from_desc()
+ * @see eina_future_cb_ignore_error()
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_log_from_desc()
+ */
+struct _Eina_Future_Desc {
+ /**
+ * Called when the future is resolved or rejected.
+ *
+ * Once a future is resolved or rejected this function is called passing the future result
+ * to inform the user that the future operation has ended. Normally this
+ * function is called from a safe context (main loop or some platform defined safe context),
+ * however in case of a future cancellation (eina_future_cancel()) or if eina_future_then(),
+ * eina_future_chain() and friends fails to create a new future,
+ * this function is called from the current context.
+ *
+ * Use this function to free @p data if necessary.
+ * @see eina_future_chain()
+ * @see eina_future_then()
+ * @see eina_future_cancel()
+ */
+ Eina_Future_Cb cb;
+ /**
+ * Context data to @p cb. The @p data should be freed inside @p cb, otherwise
+ * its memory will leak!
+ */
+ const void *data;
+
+ /**
+ * The storage will be used by Eina to store a pointer to the
+ * created future. It can be @c NULL.
+ */
+ Eina_Future **storage;
+};
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Eina_Promise Eina_Promise
+ * Creates a new promise.
+ *
+ * This function creates a new promise which can be used to create a future
+ * using eina_future_new(). Everytime a promise is created a #Eina_Promise_Cancel_Cb
+ * must be provided which is used to free resources that were created.
+ *
+ * A promise may be cancelled directly by calling
+ * @c eina_future_cancel(eina_future_new(eina_promise_new(...)))
+ * that is, cancelling any future that is chained to receive the results.
+ *
+ * However promises can be cancelled indirectly by other entities.
+ * These other entities will call `eina_future_cancel()` themselves,
+ * however you may not be aware of that. Some common sources
+ * of indirect cancellations:
+ *
+ * @li A subsystem was shutdown, cancelling all pending futures (ie: ecore_shutdown())
+ *
+ * @li An EO object was linked to the promise or future, then if the object dies (last reference
+ * is gone), then the pending promises and futures will be cancelled.
+ *
+ * @li Some other entity (library provider or library user) chained and cancelled his future,
+ * which will result in your future being cancelled.
+ *
+ * Since a promise may be canceled indirectaly (by code sections that goes beyond your scope)
+ * you should always provide a cancel callback, even if you think you'll not need it.
+ *
+ * Below there's a typical example:
+ *
+ * @code
+ * #include <Ecore.h>
+ *
+ * static void
+ * _promise_cancel(void *data, Eina_Promise *p EINA_UNUSED)
+ * {
+ * Ctx *ctx = data;
+ * //In case the promise is canceled we must stop the timer!
+ * ecore_timer_del(ctx->timer);
+ * free(ctx);
+ * }
+ *
+ * static Eina_Bool
+ * _promise_resolve(void *data)
+ * {
+ * Ctx *ctx = data;
+ * Eina_Value v;
+ * eina_value_setup(&v, EINA_VALUE_TYPE_STRING);
+ * eina_value_set(&v, "Promise resolved");
+ * eina_promise_resolve(ctx->p, v);
+ * free(ctx);
+ * return EINA_FALSE;
+ * }
+ *
+ * Eina_Promise *
+ * promise_create(Eina_Future_Scheduler *scheduler)
+ * {
+ * Ctx *ctx = malloc(sizeof(Ctx));
+ * //A timer is scheduled in order to resolve the promise
+ * ctx->timer = ecore_timer_add(122, _promise_resolve, ctx);
+ * //The _promise_cancel() will be used to clean ctx if the promise is canceled.
+ * ctx->p = eina_promise_new(scheduler, _promise_cancel, ctx);
+ * return ctx->p;
+ * }
+ * @endcode
+ *
+ * @param cancel_cb A callback used to inform that the promise was canceled. Use
+ * this callback to @c free @p data. @p cancel_cb must not be @c NULL !
+ * @param data Data to @p cancel_cb.
+ * @return A promise or @c NULL on error.
+ * @see eina_future_cancel()
+ * @see eina_future_new()
+ * @see eina_promise_resolve()
+ * @see eina_promise_reject()
+ * @see eina_promise_data_get()
+ * @see eina_promise_as_value()
+ * @see #Eina_Future_Scheduler
+ * @see #Eina_Future_Scheduler_Entry
+ * @see #Eina_Future_Scheduler_Cb
+ * @{
+ */
+EAPI Eina_Promise *eina_promise_new(Eina_Future_Scheduler *scheduler, Eina_Promise_Cancel_Cb cancel_cb, const void *data) EINA_ARG_NONNULL(1, 2) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * Gets the data attached to the promise.
+ *
+ * @return The data passed to eina_promise_new() or @c NULL on error.
+ * @see eina_promise_new()
+ * @see eina_promise_resolve()
+ * @see eina_promise_reject()
+ * @see eina_promise_as_value()
+ */
+EAPI void *eina_promise_data_get(const Eina_Promise *p) EINA_ARG_NONNULL(1);
+
+/**
+ * Resolves a promise.
+ *
+ *
+ * This function schedules an resolve event in a
+ * safe context (main loop or some platform defined safe context),
+ * whenever possible the future callbacks will be dispatched.
+ *
+ * @param p A promise to resolve.
+ * @param value The value to be delivered. Note that the value contents must survive this function scope,
+ * that is, do @b not use stack allocated blobs, arrays, structures or types that
+ * keeps references to memory you give. Values will be automatically cleaned up
+ * using @c eina_value_flush() once they are unused (no more future or futures
+ * returned a new value).
+ *
+ * @see eina_promise_new()
+ * @see eina_promise_reject()
+ * @see eina_promise_data_get()
+ * @see eina_promise_as_value()
+ */
+EAPI void eina_promise_resolve(Eina_Promise *p, Eina_Value value) EINA_ARG_NONNULL(1);
+/**
+ * Rejects a promise.
+ *
+ * This function schedules an reject event in a
+ * safe context (main loop or some platform defined safe context),
+ * whenever possible the future callbacks will be dispatched.
+ *
+ * @param p A promise to reject.
+ * @param err An Eina_Error value
+ * @note Internally this function will create an Eina_Value with type #EINA_VALUE_TYPE_ERROR.
+ *
+ * @see eina_promise_new()
+ * @see eina_promise_resolve()
+ * @see eina_promise_data_get()
+ * @see eina_promise_as_value()
+ */
+EAPI void eina_promise_reject(Eina_Promise *p, Eina_Error err) EINA_ARG_NONNULL(1);
+
+
+/**
+ * @}
+ */
+
+/**
+ * @defgroup Eina_Future Eina_Future
+ * Cancels a future.
+ *
+ * This function will cancel the whole future chain immediately (it will not be schedule to the next mainloop pass)
+ * and it will also cancel the promise linked against it. The Eina_Future_Cb will be called
+ * with an Eina_Value typed as #EINA_VALUE_TYPE_ERROR, which its value will be
+ * ECANCELED
+ * @param f The future to cancel.
+ * @{
+ */
+EAPI void eina_future_cancel(Eina_Future *f) EINA_ARG_NONNULL(1);
+
+/**
+ * Flushes an #Eina_Future_Desc
+ *
+ * This function is mainly used by bindings to flush a #Eina_Future_Desc.
+ * It will call the #Eina_Future_Cb with `ENOMEM` and zero the @p desc contents.
+ *
+ * @param desc The #Eina_Future_Desc to flush, if @c NULL this is a noop.
+ */
+EAPI void eina_future_desc_flush(Eina_Future_Desc *desc);
+
+/**
+ * Flushes an #Eina_Future_Cb_Easy_Desc
+ *
+ * This function is mainly used by bindings to flush a #Eina_Future_Cb_Easy_Desc.
+ * It will call the #Eina_Future_Error_Cb with `ENOMEM`, the #Eina_Future_Free_Cb and
+ * zero the @p desc contents.
+ *
+ * @param desc The #Eina_Future_Cb_Easy_Desc to flush, if @c NULL this is a noop.
+ */
+EAPI void eina_future_cb_easy_desc_flush(Eina_Future_Cb_Easy_Desc *desc);
+
+/**
+ * Creates a new Eina_Value from a promise.
+ *
+ * This function creates a new Eina_Value that will store a promise
+ * in it. This function is useful for dealing with promises inside
+ * a #Eina_Future_Cb. By returning an Promise Eina_Value the
+ * whole chain will wait until the promise is resolved in
+ * order to continue its execution. Example:
+ *
+ * @code
+ * static Eina_Value
+ * _file_data_ready(const *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+ * {
+ * const char *file_data;
+ * Eina_Promise *p;
+ * //It was not possible to fetch the file size.
+ * if (v.type == EINA_VALUE_TYPE_ERROR)
+ * {
+ * Eina_Error err;
+ * eina_value_get(&v, &err);
+ * fprintf(stderr, "Could get the file data. Reason: %s\n", eina_error_msg_get(err));
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ * else if (v.type != EINA_VALUE_TYPE_STRING)
+ * {
+ * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_STRING->name, v.type->name);
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ *
+ * eina_value_get(&v, &file_data);
+ * //count_words will count the words in the background and resolve the promise once it is over...
+ * p = count_words(file_data);
+ * return eina_promise_as_value(p);
+ * }
+ *
+ * static Eina_Value
+ * _word_count_ready(const *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+ * {
+ * //The _word_count_ready will only be called once count_words() resolves/rejects the promise returned by _file_data_ready()
+ * int count;
+ * if (v.type == EINA_VALUE_TYPE_ERROR)
+ * {
+ * Eina_Error err;
+ * eina_value_get(&v, &err);
+ * fprintf(stderr, "Could not count the file words. Reason: %s\n", eina_error_msg_get(err));
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ * else if (v.type != EINA_VALUE_TYPE_INT)
+ * {
+ * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_INT->name, v.type->name);
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ * eina_value_get(&v, &count);
+ * printf("File word count %d\n", count);
+ * return v;
+ * }
+ *
+ * void
+ * file_operation(void)
+ * {
+ * Eina_Future *f = get_file_data("/MyFile.txt");
+ * eina_future_chain(f, {.cb = _file_data_ready, .data = NULL},
+ * {.cb = _word_count_ready, .data = NULL});
+ * }
+ * @endcode
+ *
+ * @return An Eina_Value. On error the value's type will be @c NULL.
+ * @note If an error happens the promise will be CANCELED.
+ * @see eina_future_as_value()
+ * @see eina_promise_reject()
+ * @see eina_promise_resolve()
+ */
+EAPI Eina_Value eina_promise_as_value(Eina_Promise *p) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+/**
+ * Creates an Eina_Value from a future.
+ *
+ * This function is used for the same purposes as eina_promise_as_value(),
+ * but receives an Eina_Future instead.
+ *
+ * @param f A future to create a Eina_Value from.
+ * @return An Eina_Value. On error the value's type will be @c NULL.
+ * @note If an error happens the future @p f will be CANCELED
+ * @see eina_promise_as_value()
+ */
+EAPI Eina_Value eina_future_as_value(Eina_Future *f)EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * Creates a new future.
+ *
+ * This function creates a new future and can be used to report
+ * that an operation has succeded or failed using
+ * eina_promise_resolve() or eina_promise_reject().
+ *
+ * Futures can also be canceled using eina_future_cancel(), which
+ * will cause the whole chain to be cancelled alongside with any pending promise.
+ *
+ * @note A promise can only have one future attached to it, calling
+ * eina_future_new() on the same promise twice will
+ * result in an error (@c NULL is returned) and the future
+ * attached to the promise will be canceled!
+ *
+ * @param p A promise used to attach a future. May not be @c NULL.
+ * @return The future or @c NULL on error.
+ * @note If an error happens this function will CANCEL the promise.
+ * @see eina_promise_new()
+ * @see eina_promise_reject()
+ * @see eina_promise_resolve()
+ * @see eina_future_cancel()
+ */
+EAPI Eina_Future *eina_future_new(Eina_Promise *p) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+/**
+ * Register an Eina_Future_Desc to be used when the future is resolve/rejected.
+ *
+ * With this function a callback and data is attached to the future and then
+ * once it's resolved or rejected the callback will be informed.
+ *
+ * If during the future creation an error happens this function will return @c NULL,
+ * and the #Eina_Future_Desc::cb will be called reporting an error (`EINVAL` or `ENOMEM`),
+ * so the user has a chance to free #Eina_Future_Desc::data.
+ *
+ * In case a future in the chain is canceled, the whole chain will be canceled immediately
+ * and the error `ECANCELED` will be reported.
+ *
+ * Below there's a simple usage of this function.
+ *
+ * @code
+ * static Eina_Value
+ * _file_ready(const *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+ * {
+ * Ctx *ctx = data;
+ * //It was not possible to fetch the file size.
+ * if (v.type == EINA_VALUE_TYPE_ERROR)
+ * {
+ * Eina_Error err;
+ * eina_value_get(&v, &err);
+ * fprintf(stderr, "Could not read the file size. Reason: %s\n", eina_error_msg_get(err));
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ * else if (v.type != EINA_VALUE_TYPE_INT)
+ * {
+ * fprintf(stderr, "Expecting type '%s' - received '%s'\n", EINA_VALUE_TYPE_INT->name, v.type->name);
+ * ecore_main_loop_quit();
+ * return v;
+ * }
+ * eina_value_get(&v, &ctx->size);
+ * printf("File size is %d bytes\n", ctx->size);
+ * return v;
+ * }
+ *
+ * void
+ * file_operation(void)
+ * {
+ * Eina_Future *f = get_file_size_async("/MyFile.txt");
+ * eina_future_then_from_desc(f, (const Eina_Future_Desc){.cb = _size_ready, .data = NULL});
+ * //There's a helper macro called eina_future_then() which simplifies the usage.
+ * //The code below has the same effect.
+ * //eina_future_then(f, _size_ready, NULL);
+ * }
+ * @endcode
+ *
+ * Although the code presented at `_size_ready()` is very simple, most of it
+ * is just used to check the Eina_Value type. In order
+ * to avoid this type of code the function eina_future_cb_easy_from_desc()
+ * was created. Please, check its documentation for more information.
+ *
+ * This function can also be used to create a future chain, making
+ * it possible to execute the future result in a cascade order. When
+ * using a future chain the Eina_Value returned by a #Eina_Future_Desc::cb
+ * will be delivered to the next #Eina_Future_Desc::cb in the chain.
+ *
+ * Here's an example:
+ *
+ * static Eina_Value
+ * _future_cb1(const *data EINA_UNUSED, const Eina_Value v)
+ * {
+ * Eina_Value new_v;
+ * int i;
+ *
+ * //There's no need to check the Eina_Value type since we're using eina_future_cb_easy()
+ * eina_value_get(&v, &i);
+ * printf("File size as int: %d\n", i);
+ * eina_value_setup(&new_v, EINA_VALUE_TYPE_STRING);
+ * //Convert the file size to string
+ * eina_value_convert(&v, &new_v);
+ * return new_v;
+ * }
+ *
+ * static Eina_Value
+ * _future_cb2(const *data EINA_UNUSED, const Eina_Value v)
+ * {
+ * Eina_Value new_v;
+ * const char *file_size_str;
+ *
+ * //There's no need to check the Eina_Value type since we're using eina_future_cb_easy()
+ * eina_value_get(&v, &file_size_str);
+ * printf("File size as string: %s\n", file_size_str);
+ * eina_value_setup(&new_v, EINA_VALUE_TYPE_DOUBLE);
+ * eina_value_convert(&v, &new_v);
+ * return new_v;
+ * }
+ *
+ * static Eina_Value
+ * _future_cb3(const *data EINA_UNUSED, const Eina_Value v)
+ * {
+ * double d;
+ *
+ * //There's no need to check the Eina_Value type since we're using eina_future_cb_easy()
+ * eina_value_get(&v, &d);
+ * printf("File size as double: %g\n", d);
+ * return v;
+ * }
+ *
+ * static Eina_Value
+ * _future_err(void *data EINA_UNUSED, Eina_Error err)
+ * {
+ * //This function is called if future result type does not match or another error occurred
+ * Eina_Value new_v;
+ * eina_value_setup(&new_v, EINA_VALUE_TYPE_ERROR);
+ * eina_valuse_set(&new_v, err);
+ * fprintf(stderr, "Error during future process. Reason: %s\n", eina_error_msg_get(err));
+ * //Pass the error to the next future in the chain..
+ * return new_v;
+ * }
+ *
+ * @code
+ * void chain(void)
+ * {
+ * Eina_Future *f = get_file_size_async("/MyFile.txt");
+ * f = eina_future_then_easy(f, .success = _future_cb1, .success_type = EINA_VALUE_TYPE_INT);
+ * //_future_cb2 will be executed after _future_cb1()
+ * f = eina_future_then_easy(f, .success = _future_cb2, .success_type = EINA_VALUE_TYPE_STRING);
+ * //_future_cb2 will be executed after _future_cb2()
+ * f = eina_future_then_easy(f, .success = _future_cb3, .success_type = EINA_VALUE_TYPE_DOUBLE);
+ * //If an error happens _future_err will be called
+ * eina_future_then_easy(f, .error = _future_err);
+ * }
+ * @endcode
+ *
+ * Although it's possible to create a future chain using eina_future_then()/eina_future_then_from_desc()
+ * there's a function that makes this task much easier, please check eina_future_chain_array() for more
+ * information.
+ * @note This example does manual conversion and print, however we offer
+ * eina_future_cb_convert_to() and eina_future_cb_console_from_desc() and to make those common case easier.
+ *
+ * @param prev A future to link against
+ * @param desc A description struct contaning the callback and data.
+ * @return A new future or @c NULL on error.
+ * @note If an error happens the whole future chain will CANCELED and
+ * desc.cb will be called in order to free desc.data.
+ * @see eina_future_new()
+ * @see eina_future_then()
+ * @see #Eina_Future_Desc
+ * @see eina_future_chain_array()
+ * @see eina_future_chain()
+ * @see eina_future_cb_console_from_desc()
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ * @see eina_future_cb_convert_to()
+ * @see eina_future_cancel()
+ * @see eina_future_then_easy()
+ * @see eina_future_cb_log_from_desc()
+ */
+EAPI Eina_Future *eina_future_then_from_desc(Eina_Future *prev, const Eina_Future_Desc desc) EINA_ARG_NONNULL(1);
+
+
+/**
+ * Creates an Eina_Future_Desc that will log the previous future resolved value.
+ *
+ * This function can be used to quickly log the value that an #Eina_Future_Desc::cb
+ * is returning. The returned value will be passed to the next future in the chain without
+ * modifications.
+ *
+ * There're some helper macros like eina_future_cb_log_dbg() which will automatically
+ * fill the following fields:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_DBG will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ *
+ * @param desc The description data to be used.
+ * @return An #Eina_Future_Desc
+ *
+ * @see #Eina_Future_Cb_Log_Desc
+ * @see efl_future_then()
+ * @see efl_future_chain()
+ * @see eina_future_cb_log_dbg()
+ * @see eina_future_cb_log_crit()
+ * @see eina_future_cb_log_err()
+ * @see eina_future_cb_log_info()
+ * @see eina_future_cb_log_warn()
+ * @see eina_future_cb_console_from_desc()
+ */
+EAPI Eina_Future_Desc eina_future_cb_log_from_desc(const Eina_Future_Cb_Log_Desc desc) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * Creates a future chain.
+ *
+ *
+ * This behaves exactly like eina_future_then_from_desc(), but makes it easier to create future chains.
+ *
+ * If during the future chain creation an error happens this function will return @c NULL,
+ * and the #Eina_Future_Desc::cb from the @p descs array will be called reporting an error (`EINVAL` or `ENOMEM`),
+ * so the user has a chance to free #Eina_Future_Desc::data.
+ *
+ * Just like eina_future_then_from_desc(), the value returned by a #Eina_Future_Desc::cb callback will
+ * be delivered to the next #Eina_Future_Desc::cb in the chain.
+ *
+ * In case a future in the chain is canceled, the whole chain will be canceled immediately
+ * and the error `ECANCELED` will be reported.
+ *
+ * Here's an example:
+ *
+ * @code
+ *
+ * //callbacks code....
+ *
+ * Eina_Future* chain(void)
+ * {
+ * Eina_Future *f = get_file_size_async("/MyFile.txt");
+ * return eina_future_chain(f, eina_future_cb_easy(_future_cb1, _future_err, NULL, EINA_VALUE_TYPE_INT, NULL),
+ * eina_future_cb_easy(_future_cb2, _future_err, NULL, EINA_VALUE_TYPE_STRING, NULL),
+ * eina_future_cb_easy(_future_cb3, _future_err, NULL, EINA_VALUE_TYPE_DOUBLE, NULL),
+ * {.cb = _future_cb4, .data = NULL });
+ * }
+ * @endcode
+ *
+ * @param prev The previous future
+ * @param descs An array of descriptions. The last element of the array must have the #Eina_Future_Desc::cb set to @c NULL
+ * @return A future or @c NULL on error.
+ * @note If an error happens the whole future chain will CANCELED and
+ * desc.cb will be called in order to free desc.data.
+ * @see eina_future_new()
+ * @see eina_future_then()
+ * @see #Eina_Future_Desc
+ * @see eina_future_chain()
+ * @see eina_future_cb_ignore_error()
+ * @see eina_future_cb_console_from_desc()
+ * @see eina_future_cb_log_from_desc()
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ * @see eina_future_then_from_desc()
+ * @see eina_future_then_easy()
+ * @see eina_future_cb_convert_to()
+ */
+EAPI Eina_Future *eina_future_chain_array(Eina_Future *prev, const Eina_Future_Desc descs[]) EINA_ARG_NONNULL(1, 2);
+
+
+/**
+ *
+ * Wrappper around eina_future_chain_array() and eina_future_cb_easy_from_desc()
+ *
+ * This functions makes it easier to use eina_future_chain_array() with eina_future_cb_easy_from_desc(),
+ * check the macro eina_future_chain_easy() for an syntax sugar.
+ *
+ *
+ * @param prev The previous future
+ * @param descs An array of descriptions. The last element of the array must have the #Eina_Future_Desc::cb set to @c NULL
+ * @return A future or @c NULL on error.
+ * @note If an error happens the whole future chain will CANCELED and
+ * desc.cb will be called in order to free desc.data.
+ * @see eina_future_chain_easy()
+ * @see eina_future_chain()
+ * @see eina_future_cb_easy()
+ * @see eina_future_chain_array()
+ * @see eina_future_cb_easy_from_desc()
+ */
+EAPI Eina_Future *eina_future_chain_easy_array(Eina_Future *prev, const Eina_Future_Cb_Easy_Desc descs[]) EINA_ARG_NONNULL(1, 2);
+
+/**
+ * Creates an Eina_Future_Desc that will print the previous future resolved value.
+ *
+ * This function can be used to quickly inspect the value that an #Eina_Future_Desc::cb
+ * is returning. The returned value will be passed to the next future in the chain without
+ * modifications.
+ *
+ * There's also an helper macro called eina_future_cb_console() which makes this
+ * fuction easier to use.
+ *
+ * Example:
+ *
+ * @code
+ *
+ * eina_future_chain(a_future, {.cb = cb1, .data = NULL},
+ * //Inspect the cb1 value and pass to cb2 without modifications
+ * eina_future_cb_console("cb1 value:"),
+ * {.cb = cb2, .data = NULL},
+ * //Inspect the cb2 value
+ * eina_future_cb_console("cb2 value:", " cb2 value suffix\n"))
+ * @endcode
+ *
+ * @param prefix A Prefix to print, if @c NULL an empty string ("") is used.
+ * @param suffix A suffix to print. If @c NULL '\n' will be used. If suffix is provided
+ * the '\n' must be provided by suffix otherwise the printed text will not contain
+ * a line feed.
+ * @return An #Eina_Future_Desc
+ * @see eina_future_then()
+ * @see #Eina_Future_Desc
+ * @see eina_future_chain()
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ * @see eina_future_then_from_desc()
+ * @see eina_future_then_easy()
+ * @see eina_future_cb_console()
+ * @see eina_future_cb_ignore_error()
+ * @see eina_future_cb_log_from_desc()
+ */
+EAPI Eina_Future_Desc eina_future_cb_console_from_desc(const Eina_Future_Cb_Console_Desc desc) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * Returns a #Eina_Future_Desc that ignores an error.
+ *
+ * This function may be used if one wants to ignore an error. If the error
+ * specified error happens an EINA_VALUE_EMPTY will be delivered to the
+ * next future in the chain.
+ *
+ * @param err The error to be ignored.
+ * @param A future descriptor to be used with eina_future_then() or eina_future_chain()
+ */
+EAPI Eina_Future_Desc eina_future_cb_ignore_error(Eina_Error err);
+
+/**
+ * Creates an #Eina_Future_Desc which will convert the the received eina value to a given type.
+ *
+ * @param type The Eina_Value_Type to convert to.
+ * @return An #Eina_Future_Desc
+ * @see eina_future_then()
+ * @see #Eina_Future_Desc
+ * @see eina_future_chain()
+ * @see eina_future_cb_easy_from_desc()
+ * @see eina_future_cb_easy()
+ * @see eina_future_then_from_desc()
+ * @see eina_future_then_easy()
+ */
+EAPI Eina_Future_Desc eina_future_cb_convert_to(const Eina_Value_Type *type);
+
+/**
+ * Creates an #Eina_Future_Desc based on a #Eina_Future_Cb_Easy_Desc
+ *
+ * This function aims to be used in conjuction with eina_future_chain(),
+ * eina_future_then_from_desc() and friends and its main objective is to simplify
+ * error handling and Eina_Value type checks.
+ * It uses three callbacks to inform the user about the future's
+ * result and life cycle. They are:
+ *
+ * @li #Eina_Future_Cb_Easy_Desc::success: This callback is called when
+ * the future execution was successful, this is, no errors occurred and
+ * the result type matches #Eina_Future_Cb_Easy_Desc::success_type. In case
+ * #Eina_Future_Cb_Easy_Desc::success_type is @c NULL, this function will
+ * only be called if the future did not report an error. The value returned
+ * by this function will be propagated to the next future in the chain (if any).
+ *
+ * @li #Eina_Future_Cb_Easy_Desc::error: This callback is called when
+ * the future result is an error or #Eina_Future_Cb_Easy_Desc::success_type
+ * does not match the future result type. The value returned
+ * by this function will be propagated to the next future in the chain (if any).
+ *
+ * @li #Eina_Future_Cb_Easy_Desc::free: Called after the future was freed and any resources
+ * allocated must be freed at this point. This callback is always called.
+ *
+ * Check the example below for a sample usage:
+ *
+ * @code
+ * static Eina_Value
+ * _file_size_ok(void *data, Eina_Value v)
+ * {
+ * Ctx *ctx = data;
+ * //Since an Eina_Future_Cb_Easy_Desc::success_type was provided, there's no need to check the value type
+ * int s;
+ * eina_value_get(&v, &s);
+ * printf("File size is %d bytes\n", s);
+ * ctx->file_size = s;
+ * return v;
+ * }
+ *
+ * static Eina_Value
+ * _file_size_err(void *data, Eina_Error err)
+ * {
+ * fprintf(stderr, "Could not read the file size. Reason: %s\n", eina_error_msg_get(err));
+ * //Stop propagating the error.
+ * return EINA_VALUE_EMPTY;
+ * }
+ *
+ * static void
+ * _future_freed(void *data, const Eina_Future dead)
+ * {
+ * Ctx *ctx = data;
+ * printf("Future %p deleted\n", dead);
+ * ctx->file_size_handler_cb(ctx->file_size);
+ * free(ctx);
+ * }
+ *
+ * @code
+ * void do_work(File_Size_Handler_Cb cb)
+ * {
+ * Ctx *ctx = malloc(sizeof(Ctx));
+ * ctx->f = get_file_size("/tmp/todo.txt");
+ * ctx->file_size = -1;
+ * ctx->file_size_handler_cb = cb;
+ * eina_future_then_easy(f, _file_size_ok, _file_size_err, _future_freed, EINA_VALUE_TYPE_INT, ctx);
+ * }
+ * @endcode
+ *
+ * @return An #Eina_Future_Desc
+ * @see eina_future_chain()
+ * @see eina_future_then()
+ * @see eina_future_cb_easy()
+ */
+EAPI Eina_Future_Desc eina_future_cb_easy_from_desc(const Eina_Future_Cb_Easy_Desc desc) EINA_WARN_UNUSED_RESULT;
+/**
+ * Creates an all promise.
+ *
+ * Creates a promise that is resolved once all the futures
+ * from the @p array are resolved.
+ * The promise is resolved with an Eina_Value type array which
+ * contains EINA_VALUE_TYPE_VALUE elements. The result array is
+ * ordered according to the @p array argument. Example:
+ *
+ * @code
+ *
+ * static const char *
+ * _get_operation_name_by_index(int idx)
+ * {
+ * switch (idx)
+ * {
+ * case 0: return "Get file data";
+ * case 1: return "Get file size";
+ * default: return "sum";
+ * }
+ * }
+ *
+ * static Eina_Value
+ * _all_cb(const void *data EINA_UNUSED, const Eina_Value array, const Eina_Future *dead EINA_UNUSED)
+ * {
+ * Eina_Error err;
+ * unsined int i, len;
+ *
+ * if (v.type == EINA_VALUE_TYPE_ERROR)
+ * {
+ * eina_value_get(&array, &err);
+ * fprintf(stderr, "Could not complete all operations. Reason: %s\n", eina_error_msg_get(err));
+ * return array;
+ * }
+ * len = eina_value_array_count(&array);
+ * for (i = 0; i < len; i++)
+ * {
+ * Eina_Value v;
+ * eina_value_array_get(&array, i, &v);
+ * if (v.type == EINA_VALUE_TYPE_ERROR)
+ * {
+ * eina_value_get(&array, &err);
+ * fprintf(stderr, "Could not complete operation '%s'. Reason: %s\n", _get_operation_name_by_index(i), eina_error_msg_get(err));
+ * continue;
+ * }
+ * if (!i)
+ * {
+ * const char *msg;
+ * if (v.type != EINA_VALUE_TYPE_STRING)
+ * {
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_STRING->name, v.type->name);
+ * continue;
+ * }
+ * eina_value_get(&v, &msg);
+ * printf("File content:%s\n", msg);
+ * }
+ * else if (i == 1)
+ * {
+ * int i;
+ * if (v.type != EINA_VALUE_TYPE_INT)
+ * {
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_INT->name, v.type->name);
+ * continue;
+ * }
+ * eina_value_get(&v, &i);
+ * printf("File size: %d\n", i);
+ * }
+ * else
+ * {
+ * double p;
+ * if (v.type != EINA_VALUE_TYPE_DOUBLE)
+ * {
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_DOUBLE->name, v.type->name);
+ * continue;
+ * }
+ * eina_value_get(&v, &p);
+ * printf("50 places of PI: %f\n", p);
+ * }
+ * }
+ * return array;
+ * }
+ *
+ * void func(void)
+ * {
+ * Eina_Future *f1, *f2, *f3, f_all;
+ *
+ * f1 = read_file("/tmp/todo.txt");
+ * f2 = get_file_size("/tmp/file.txt");
+ * //calculates 50 places of PI
+ * f3 = calc_pi(50);
+ * f_all = eina_future_all(f1, f2, f3);
+ * eina_future_then(f_all, _all_cb, NULL);
+ * }
+ * @endcode
+ *
+ * @param array An array of futures, @c #EINA_FUTURE_SENTINEL terminated.
+ * @return A promise or @c NULL on error.
+ * @note On error all the futures will be CANCELED.
+ * @see eina_future_all_array()
+ */
+EAPI Eina_Promise *eina_promise_all_array(Eina_Future *array[]) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * Creates a race promise.
+ *
+ * Creates a promise that resolves when a future from the @p array
+ * is completed. The remaining futures will be canceled with the
+ * error code `ECANCELED`
+ *
+ * The resulting value is an EINA_VALUE_TYPE_STRUCT with two fields:
+ *
+ * @li A field named "value" which contains an Eina_Value with the result itself.
+ * @li A field named "index" which is an int that contains the index of the completed
+ * function relative to the @p array;
+ *
+ * Example.
+ *
+ * @code
+ *
+ * static const char *
+ * _get_operation_name_by_index(int idx)
+ * {
+ * switch (idx)
+ * {
+ * case 0: return "Get file data";
+ * case 1: return "Get file size";
+ * default: return "sum";
+ * }
+ * }
+ *
+ * static Eina_Value
+ * _race_cb(const void *data EINA_UNUSED, const Eina_Value v)
+ * {
+ * unsigned int i;
+ * Eina_Value result;
+ * Eina_Error err;
+ * Eina_Value_Struct *st;
+ *
+ * //No need to check for the 'v' type. eina_future_cb_easy() did that for us,
+ * //However we should check if the struct has the correct description
+ * st = eina_value_memory_get(&v);
+ * if (st->desc != EINA_PROMISE_RACE_STRUCT_DESC)
+ * {
+ * fprintf(stderr, "Eina_Value is not a race struct\n");
+ * return v;
+ * }
+ * eina_value_struct_get(&v, "index", &i);
+ * //Get the operation result
+ * eina_value_struct_get(&v, "value", &result);
+ * if (!i)
+ * {
+ * const char *msg;
+ * if (result.type != EINA_VALUE_TYPE_STRING)
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_STRING->name, result.type->name);
+ * else
+ * {
+ * eina_value_get(&result, &msg);
+ * printf("File content:%s\n", msg);
+ * }
+ * }
+ * else if (i == 1)
+ * {
+ * int i;
+ * if (result.type != EINA_VALUE_TYPE_INT)
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_INT->name, v.type->name);
+ * else
+ * {
+ * eina_value_get(&result, &i);
+ * printf("File size: %d\n", i);
+ * }
+ * }
+ * else
+ * {
+ * double p;
+ * if (result.type != EINA_VALUE_TYPE_DOUBLE)
+ * fprintf(stderr, "Operation %s expects '%s' - received '%s'\n", _get_operation_name_by_index(i), EINA_VALUE_TYPE_DOUBLE->name, result.type->name);
+ * else
+ * {
+ * eina_value_get(&result, &p);
+ * printf("50 places of PI: %f\n", p);
+ * }
+ * }
+ * eina_value_flush(&result);
+ * return v;
+ * }
+ *
+ * static Eina_Value
+ * _race_err(void *data, Eina_Error err)
+ * {
+ * fprintf(stderr, "Could not complete the race future. Reason: %s\n", eina_error_msg_get(err));
+ * return EINA_VALUE_EMPTY;
+ * }
+ *
+ * void func(void)
+ * {
+ * static const *Eina_Future[] = {NULL, NULL, NULL, NULL};
+ * Eina_List *l = NULL;
+ *
+ * futures[0] = read_file("/tmp/todo.txt");
+ * futures[1] = get_file_size("/tmp/file.txt");
+ * //calculates 50 places of PI
+ * futures[2] = calc_pi(50);
+ * f_race = eina_future_race_array(futures);
+ * eina_future_then_easy(f_race, _race_cb, _race_err, NULL, EINA_VALUE_TYPE_STRUCT, NULL);
+ * }
+ * @endcode
+ *
+ * @param array An array of futures, @c #EINA_FUTURE_SENTINEL terminated.
+ * @return A promise or @c NULL on error.
+ * @note On error all the futures will be CANCELED.
+ * @see eina_future_race_array()
+ * @see #_Eina_Future_Race_Result
+ */
+EAPI Eina_Promise *eina_promise_race_array(Eina_Future *array[]) EINA_ARG_NONNULL(1) EINA_WARN_UNUSED_RESULT;
+
+/**
+ * @struct The struct that is used to store the race result.
+ *
+ * When using eina_promise_race_array() and friends, the future result
+ * will be reported as a struct. The values can be obtained using
+ * eina_value_struct_get() or one could access the struct directly
+ * such as this example:
+ *
+ * @code
+ * static Eina_Value
+ * _race_cb(const void *data EINA_UNUSED, const Eina_Value v)
+ * {
+ * //code...
+ * Eina_Value_Struct st;
+ * Eina_Future_Race_Result *rr;
+ * eina_value_get(v, &st);
+ * rr = st.memory;
+ * printf("Winning future index: %u\n", rr->index);
+ * //more code..
+ * return v;
+ * }
+ * @endcode
+ *
+ * @see eina_promise_race_array()
+ * @see eina_future_race_array()
+ * @see eina_promise_race()
+ * @see eina_future_race()
+ * @see #EINA_PROMISE_RACE_STRUCT_DESC
+ */
+struct _Eina_Future_Race_Result {
+ /**
+ * The race result.
+ */
+ Eina_Value value;
+ /**
+ * The future index that won the race.
+ */
+ unsigned int index;
+};
+
+/**
+ * @var EINA_PROMISE_RACE_STRUCT_DESC
+ *
+ * A pointer to the race struct description, which
+ * is used by eina_promise_race_array();
+ *
+ * This struct contains two members:
+ * @li value An EINA_VALUE_TYPE_VALUE that contains the future result that wont the race.
+ * @li index An EINA_VALUE_TYPE_UINT that contains the future index that won the race.
+ *
+ * @see eina_promise_race_array()
+ * @see #_Eina_Future_Race_Result
+ */
+EAPI extern const Eina_Value_Struct_Desc *EINA_PROMISE_RACE_STRUCT_DESC;
+
+/**
+ * Creates a future that will be resolved once all futures from @p array is resolved.
+ * This is a helper over eina_promise_all_array()
+ *
+ * @param array A future array, must be terminated with #EINA_FUTURE_SENTINEL
+ * @return A future.
+ * @see eina_promise_all_array()
+ * @see #EINA_FUTURE_SENTINEL
+ */
+static inline Eina_Future *
+eina_future_all_array(Eina_Future *array[])
+{
+ Eina_Promise *p = eina_promise_all_array(array);
+ if (!p) return NULL;
+ return eina_future_new(p);
+}
+
+/**
+ * Creates a future that will be resolved once a future @p array is resolved.
+ * This is a helper over eina_promise_race_array()
+ *
+ * @param array A future array, must be terminated with #EINA_FUTURE_SENTINEL
+ * @return A future.
+ * @see eina_promise_race_array()
+ * @see #EINA_FUTURE_SENTINEL
+ */
+static inline Eina_Future *
+eina_future_race_array(Eina_Future *array[])
+{
+ Eina_Promise *p = eina_promise_race_array(array);
+ if (!p) return NULL;
+ return eina_future_new(p);
+}
+
+/**
+ * Used by eina_promise_race_array() and eina_promise_all_array() and
+ * friends to flag the end of the array.
+ *
+ * @see eina_promise_race_array()
+ * @see eina_promise_all_array()
+ */
+#define EINA_FUTURE_SENTINEL ((void *)(unsigned long)-1)
+
+/**
+ * A syntatic sugar over eina_promise_race_array().
+ * Usage:
+ * @code
+ * promise = eina_promise_race(future1, future2, future3, future4);
+ * @endcode
+ * @see eina_promise_race_array()
+ */
+#define eina_promise_race(...) eina_promise_race_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL})
+/**
+ * A syntatic sugar over eina_future_race_array().
+ * Usage:
+ * @code
+ * future = eina_future_race(future1, future2, future3, future4);
+ * @endcode
+ * @see eina_future_racec_array()
+ */
+#define eina_future_race(...) eina_future_race_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL})
+/**
+ * A syntatic sugar over eina_future_all_array().
+ * Usage:
+ * @code
+ * future = eina_future_all(future1, future2, future3, future4);
+ * @endcode
+ * @see eina_future_all_array()
+ */
+#define eina_future_all(...) eina_future_all_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL})
+/**
+ * A syntatic sugar over eina_promise_all_array().
+ * Usage:
+ * @code
+ * promise = eina_promise_all(future1, future2, future3, future4);
+ * @endcode
+ * @see eina_promise_all_array()
+ */
+#define eina_promise_all(...) eina_promise_all_array((Eina_Future *[]){__VA_ARGS__, EINA_FUTURE_SENTINEL})
+/**
+ * A syntatic sugar over eina_future_cb_easy_from_desc().
+ * Usage:
+ * @code
+ * future_desc = eina_future_cb_easy(_success_cb, _error_cb, _free_cb, EINA_VALUE_TYPE_INT, my_data);
+ * @endcode
+ * @see eina_future_cb_easy_from_desc()
+ */
+#define eina_future_cb_easy(...) eina_future_cb_easy_from_desc((Eina_Future_Cb_Easy_Desc){__VA_ARGS__})
+/**
+ * A syntatic sugar over eina_future_chain_array().
+ * Usage:
+ * @code
+ * future = eina_future_chain(future, {.cb = _my_cb, .data = my_data}, {.cb = _my_another_cb, .data = NULL});
+ * @endcode
+ * @see eina_future_chain_array()
+ */
+#define eina_future_chain(_prev, ...) eina_future_chain_array(_prev, (Eina_Future_Desc[]){__VA_ARGS__, {.cb = NULL, .data = NULL}})
+/**
+ * A syntatic sugar over eina_future_then_from_desc().
+ * Usage:
+ * @code
+ * future = eina_future_then(future, _my_cb, my_data);
+ * @endcode
+ * @see eina_future_then_from_desc()
+ * @see eina_future_then_easy()
+ */
+#define eina_future_then(_prev, ...) eina_future_then_from_desc(_prev, (Eina_Future_Desc){__VA_ARGS__})
+/**
+ * A syntatic sugar over eina_future_cb_console_from_desc().
+ * Usage:
+ * @code
+ * desc = eina_future_cb_console(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_console_from_desc()
+ */
+#define eina_future_cb_console(...) eina_future_cb_console_from_desc((Eina_Future_Cb_Console_Desc){__VA_ARGS__})
+
+/**
+ * A syntatic sugar over eina_future_cb_log_from_desc().
+ *
+ * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_DBG will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ * Usage:
+ * @code
+ * desc = eina_future_cb_log_dbg(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_log_from_desc()
+ */
+#define eina_future_cb_log_dbg(_prefix, _suffix) \
+ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \
+ __FUNCTION__, EINA_LOG_LEVEL_DBG, EINA_LOG_DOMAIN_DEFAULT, __LINE__})
+
+/**
+ * A syntatic sugar over eina_future_cb_log_from_desc().
+ *
+ * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_CRITICAL will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ * Usage:
+ * @code
+ * desc = eina_future_cb_log_crit(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_log_from_desc()
+ */
+#define eina_future_cb_log_crit(_prefix, _suffix) \
+ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \
+ __FUNCTION__, EINA_LOG_LEVEL_CRITICAL, EINA_LOG_DOMAIN_DEFAULT, __LINE__})
+
+/**
+ * A syntatic sugar over eina_future_cb_log_from_desc().
+ *
+ * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_ERR will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ * Usage:
+ * @code
+ * desc = eina_future_cb_log_err(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_log_from_desc()
+ */
+#define eina_future_cb_log_err(_prefix, _suffix) \
+ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \
+ __FUNCTION__, EINA_LOG_LEVEL_ERR, EINA_LOG_DOMAIN_DEFAULT, __LINE__})
+
+/**
+ * A syntatic sugar over eina_future_cb_log_from_desc().
+ *
+ * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_INFO will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ *
+ * Usage:
+ * @code
+ * desc = eina_future_cb_log_info(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_log_from_desc()
+ */
+#define eina_future_cb_log_info(_prefix, _suffix) \
+ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \
+ __FUNCTION__, EINA_LOG_LEVEL_INFO, EINA_LOG_DOMAIN_DEFAULT, __LINE__})
+
+/**
+ * A syntatic sugar over eina_future_cb_log_from_desc().
+ *
+ * This macro will set the following fields of the #Eina_Future_Cb_Log_Desc:
+ *
+ * @li #Eina_Future_Cb_Log_Desc::file: The __FILE__ function will be used.
+ * @li #Eina_Future_Cb_Log_Desc::func: The __FUNCTION__ macro will be used.
+ * @li #Eina_Future_Cb_Log_Desc::level: EINA_LOG_LEVEL_WARN will be used.
+ * @li #Eina_Future_Cb_Log_Desc::domain: EINA_LOG_DOMAIN_DEFAULT will be used.
+ * @li #Eina_Future_Cb_Log_Desc::line: The __LINE__ macro will be used.
+ *
+ * Usage:
+ * @code
+ * desc = eina_future_cb_log_warn(.prefix = "prefix", .suffix = "suffix");
+ * @endcode
+ * @see eina_future_cb_log_from_desc()
+ */
+#define eina_future_cb_log_warn(_prefix, _suffix) \
+ eina_future_cb_log_from_desc((Eina_Future_Cb_Log_Desc){_prefix, _suffix, __FILE__, \
+ __FUNCTION__, EINA_LOG_LEVEL_WARN, EINA_LOG_DOMAIN_DEFAULT, __LINE__})
+
+/**
+ * A syntatic sugar over eina_future_then() and eina_future_cb_easy().
+ * Usage:
+ * @code
+ * f = eina_future_then_easy(f, .success = _success_cb, .success_type = EINA_VALUE_TYPE_DOUBLE, .data = NULL, );
+ * @endcode
+ * @see eina_future_then_from_desc()
+ * @see eina_future_easy()
+ * @see eina_future_then()
+ * @see eina_future_cb_easy_from_desc()
+ */
+#define eina_future_then_easy(_prev, ...) eina_future_then_from_desc(_prev, eina_future_cb_easy(__VA_ARGS__))
+
+/**
+ * A syntatic sugar over eina_future_chain() and eina_future_cb_easy().
+ * Usage:
+ * @code
+ * f = eina_future_chain_easy(f, {.success = _success_cb, .success_type = EINA_VALUE_TYPE_DOUBLE, .data = NULL},
+ * { .success = _success2_cb }, {.error = error_cb});
+ * @endcode
+ * @see eina_future_chain_array()
+ * @see eina_future_easy()
+ * @see eina_future_chain_easy_array()
+ * @see eina_future_cb_easy_from_desc()
+ */
+#define eina_future_chain_easy(_prev, ...) eina_future_chain_easy_array(_prev, (Eina_Future_Cb_Easy_Desc[]) {__VA_ARGS__, {NULL, NULL, NULL, NULL, NULL}})
+
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/lib/eina/eina_promise_private.h b/src/lib/eina/eina_promise_private.h
new file mode 100644
index 0000000000..31375b5794
--- /dev/null
+++ b/src/lib/eina/eina_promise_private.h
@@ -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
diff --git a/src/lib/eo/eina_types.eot b/src/lib/eo/eina_types.eot
index 40fbbf2df8..83be600926 100644
--- a/src/lib/eo/eina_types.eot
+++ b/src/lib/eo/eina_types.eot
@@ -62,3 +62,84 @@ struct @extern Eina.Rw_Slice {
}
struct @extern Eina.Value.Type; [[Eina value type]]
+
+struct @extern Eina.Future; [[Eina_Future handle]]
+struct @extern Eina.Promise; [[Eina_Promise handle]]
+struct @extern @free(eina_future_desc_flush) Eina_Future_Desc; [[A struct used to define a callback and data for a future.]]
+struct @extern @free(eina_future_cb_easy_desc_flush) Eina_Future_Cb_Easy_Desc; [[A struct with callbacks to be used by eina_future_cb_easy_from_desc() and eina_future_cb_easy()]]
+struct @extern Eina_Future_Cb_Console_Desc; [[A struct used to define the prefix and suffix to be printed
+ along side the a future value. This struct is used by
+ eina_future_cb_console_from_desc()]]
+struct @extern Eina_Future_Schedule_Entry; [[A struct that represents an scheduled event.
+ This struct may be used by Eina to cancel
+ a scheduled future.]]
+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.]]
+
+function @extern Eina.Future.Cb {
+ params {
+ value: const(generic_value); [[An Eina_Value which contains the operation result. Before using
+ the value, its type must be checked in order to avoid errors. This is needed, because
+ if an operation fails the Eina_Value type will be EINA_VALUE_TYPE_ERROR
+ which is a different type than the expected operation result.]]
+ dead_ptr: const(ptr(Eina.Future)); [[A pointer to the future that was completed]]
+ }
+ return: generic_value; [[An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ If there is no need to convert the received value, it's recommended
+ to pass-thru value argument. If you need to convert to a different type
+ or generate a new value, use eina_value_setup() on another Eina_Value
+ and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ whole chain will wait until the promise is resolved in order to continue its execution.
+ Note that the value contents must survive this function scope,
+ that is, do not use stack allocated blobs, arrays, structures or types that
+ keeps references to memory you give. Values will be automatically cleaned up
+ using eina_value_flush() once they are unused (no more future or futures
+ returned a new value).]]
+};
+
+function @extern Eina.Promise.Cancel.Cb {
+ params {
+ dead_promise: const(ptr(Eina.Promise)); [[The canceled promise.]]
+ }
+};
+
+function @extern Eina.Future.Success.Cb {
+ params {
+ value: const(generic_value); [[The operation result]]
+ }
+ return: generic_value; [[An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ If there is no need to convert the received value, it's recommended
+ to pass-thru value argument. If you need to convert to a different type
+ or generate a new value, use eina_value_setup() on another Eina_Value
+ and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ whole chain will wait until the promise is resolved in order to continue its execution.
+ Note that the value contents must survive this function scope,
+ that is, do not use stack allocated blobs, arrays, structures or types that
+ keeps references to memory you give. Values will be automatically cleaned up
+ using eina_value_flush() once they are unused (no more future or futures
+ returned a new value).]]
+};
+
+function @extern Eina.Future.Error.Cb {
+ params {
+ error: const(Eina.Error); [[The operation error]]
+ }
+ return: generic_value; [[An Eina_Value to pass to the next Eina_Future in the chain (if any).
+ If there is no need to convert the received value, it's recommended
+ to pass-thru value argument. If you need to convert to a different type
+ or generate a new value, use eina_value_setup() on another Eina_Value
+ and return it. By returning an promise Eina_Value (eina_promise_as_value()) the
+ whole chain will wait until the promise is resolved in order to continue its execution.
+ Note that the value contents must survive this function scope,
+ that is, do not use stack allocated blobs, arrays, structures or types that
+ keeps references to memory you give. Values will be automatically cleaned up
+ using eina_value_flush() once they are unused (no more future or futures
+ returned a new value).]]
+};
+
+function @extern Eina.Future.Free.Cb {
+ params {
+ dead_future: const(ptr(Eina.Future)); [[The future that was freed]]
+ }
+};