Eina: Add Eina_Promise/Eina_Future.
authorGuilherme Iscaro <iscaro@profusion.mobi>
Tue, 8 Aug 2017 21:10:36 +0000 (18:10 -0300)
committerGuilherme Iscaro <iscaro@profusion.mobi>
Mon, 4 Sep 2017 13:24:00 +0000 (10:24 -0300)
This commit adds a new promise/future API which aims to replace
efl_future.

12 files changed:
src/Makefile_Eina.am
src/lib/ecore/ecore.c
src/lib/ecore/ecore_events.c
src/lib/ecore/ecore_main.c
src/lib/ecore/ecore_private.h
src/lib/ecore/efl_loop.eo
src/lib/eina/Eina.h
src/lib/eina/eina_main.c
src/lib/eina/eina_promise.c [new file with mode: 0644]
src/lib/eina/eina_promise.h [new file with mode: 0644]
src/lib/eina/eina_promise_private.h [new file with mode: 0644]
src/lib/eo/eina_types.eot

index 544ce0f..bd74cdc 100644 (file)
@@ -10,6 +10,7 @@ lib/eina/eina_config.h
 
 installed_einaheadersdir = $(includedir)/eina-@VMAJ@/eina
 dist_installed_einaheaders_DATA = \
+lib/eina/eina_promise.h \
 lib/eina/eina_safety_checks.h \
 lib/eina/eina_error.h \
 lib/eina/eina_debug.h \
@@ -150,6 +151,8 @@ lib/eina/eina_mempool.c \
 lib/eina/eina_mmap.c \
 lib/eina/eina_module.c \
 lib/eina/eina_prefix.c \
+lib/eina/eina_promise.c \
+lib/eina/eina_promise_private.h \
 lib/eina/eina_quad.c \
 lib/eina/eina_quadtree.c \
 lib/eina/eina_rbtree.c \
index aa5e4e4..ec733b2 100644 (file)
@@ -251,6 +251,7 @@ ecore_init(void)
    if (getenv("ECORE_FPS_DEBUG")) _ecore_fps_debug = 1;
    if (_ecore_fps_debug) _ecore_fps_debug_init();
    if (!ecore_mempool_init()) goto shutdown_mempool;
+   if (!_ecore_event_init()) goto shutdown_mempool;
    _ecore_main_loop_init();
 
    vpath = efl_add(EFL_VPATH_CORE_CLASS, NULL);
index 3b13ef9..ed405ed 100644 (file)
@@ -48,6 +48,15 @@ struct _Ecore_Event
 };
 GENERIC_ALLOC_SIZE_DECLARE(Ecore_Event);
 
+typedef struct _Ecore_Future_Schedule_Entry
+{
+   Eina_Future_Schedule_Entry base;
+   Eina_Future_Scheduler_Cb cb;
+   Eina_Future *future;
+   Ecore_Event *event;
+   Eina_Value value;
+} Ecore_Future_Schedule_Entry;
+
 static int events_num = 0;
 static Ecore_Event *events = NULL;
 static Ecore_Event *event_current = NULL;
@@ -68,6 +77,10 @@ static int event_filters_delete_me = 0;
 static int event_id_max = ECORE_EVENT_COUNT;
 static int ecore_raw_event_type = ECORE_EVENT_NONE;
 static void *ecore_raw_event_event = NULL;
+static Ecore_Event_Handler *future_handler = NULL;
+static int ECORE_EV_FUTURE_ID = -1;
+static Eina_Mempool *mp_future_schedule_entry = NULL;
+static Eina_Bool shutting_down = EINA_FALSE;
 
 static void  _ecore_event_purge_deleted(void);
 static void *_ecore_event_del(Ecore_Event *event);
@@ -268,6 +281,100 @@ _ecore_event_handler_del(Ecore_Event_Handler *event_handler)
    return event_handler->data;
 }
 
+static Eina_Bool
+ecore_future_dispatched(void *data EINA_UNUSED, int type  EINA_UNUSED, void *event)
+{
+   Ecore_Future_Schedule_Entry *entry = event;
+   entry->event = NULL;
+   entry->cb(entry->future, entry->value);
+   return EINA_FALSE;
+}
+
+static void
+ecore_future_free(void *user_data, void *func_data EINA_UNUSED)
+{
+   Ecore_Future_Schedule_Entry *entry = user_data;
+   /*
+     In case entry->event is not NULL, it means
+     that ecore is shutting down. In this case,
+     we must cancel the future otherwise Eina may
+     try to use it and lead to crashes.
+    */
+   if (entry->event)
+     {
+        eina_future_cancel(entry->future);
+        eina_value_flush(&entry->value);
+     }
+   eina_mempool_free(mp_future_schedule_entry, entry);
+}
+
+static Eina_Future_Schedule_Entry *
+ecore_future_schedule(Eina_Future_Scheduler *sched, Eina_Future_Scheduler_Cb cb, Eina_Future *future, Eina_Value value)
+{
+   Ecore_Future_Schedule_Entry *entry = eina_mempool_malloc(mp_future_schedule_entry,
+                                                            sizeof(Ecore_Future_Schedule_Entry));
+   EINA_SAFETY_ON_NULL_RETURN_VAL(entry, NULL);
+   entry->base.scheduler = sched;
+   entry->cb = cb;
+   entry->future = future;
+   entry->value = value;
+   entry->event = ecore_event_add(ECORE_EV_FUTURE_ID, entry, ecore_future_free, entry);
+   EINA_SAFETY_ON_NULL_GOTO(entry->event, err);
+   return &entry->base;
+
+  err:
+   eina_mempool_free(mp_future_schedule_entry, entry);
+   return NULL;
+}
+
+static void
+ecore_future_recall(Eina_Future_Schedule_Entry *s_entry)
+{
+   if (shutting_down) return;
+   Ecore_Future_Schedule_Entry *entry = (Ecore_Future_Schedule_Entry *)s_entry;
+   EINA_SAFETY_ON_NULL_RETURN(entry->event);
+   ecore_event_del(entry->event);
+   eina_value_flush(&entry->value);
+   entry->event = NULL;
+}
+
+static Eina_Future_Scheduler ecore_future_scheduler = {
+   .schedule = ecore_future_schedule,
+   .recall = ecore_future_recall,
+};
+
+Eina_Future_Scheduler *
+_ecore_event_future_scheduler_get(void)
+{
+   return &ecore_future_scheduler;
+}
+
+Eina_Bool
+_ecore_event_init(void)
+{
+   const char *choice = getenv("EINA_MEMPOOL");
+   if ((!choice) || (!choice[0])) choice = "chained_mempool";
+
+   shutting_down = EINA_FALSE;
+   ECORE_EV_FUTURE_ID = ecore_event_type_new();
+   future_handler = ecore_event_handler_add(ECORE_EV_FUTURE_ID, ecore_future_dispatched, NULL);
+   EINA_SAFETY_ON_NULL_GOTO(future_handler, err_handler);
+   //FIXME: Is 512 too high?
+   mp_future_schedule_entry = eina_mempool_add(choice, "Ecore_Future_Event",
+                                               NULL, sizeof(Ecore_Future_Schedule_Entry),
+                                               512);
+   EINA_SAFETY_ON_NULL_GOTO(mp_future_schedule_entry, err_pool);
+
+   return EINA_TRUE;
+
+ err_pool:
+   ecore_event_handler_del(future_handler);
+   future_handler = NULL;
+ err_handler:
+   ECORE_EV_FUTURE_ID = -1;
+   return EINA_FALSE;
+}
+
 void
 _ecore_event_shutdown(void)
 {
@@ -275,6 +382,9 @@ _ecore_event_shutdown(void)
    Ecore_Event_Handler *eh;
    Ecore_Event_Filter *ef;
 
+   shutting_down = EINA_TRUE;
+   ecore_event_handler_del(future_handler);
+   future_handler = NULL;
    while (events) _ecore_event_del(events);
    event_current = NULL;
    for (i = 0; i < event_handlers_num; i++)
@@ -301,6 +411,7 @@ _ecore_event_shutdown(void)
    event_filters_delete_me = 0;
    event_filter_current = NULL;
    event_filter_event_current = NULL;
+   ECORE_EV_FUTURE_ID = -1;
 }
 
 int
index 12e8cf4..0bb15e0 100644 (file)
@@ -3286,5 +3286,12 @@ _efl_loop_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
    return &version;
 }
 
+EOLIAN static Eina_Future_Scheduler *
+_efl_loop_future_scheduler_get(Eo *obj EINA_UNUSED,
+                               Efl_Loop_Data *pd EINA_UNUSED)
+{
+   return _ecore_event_future_scheduler_get();
+}
+
 
 #include "efl_loop.eo.c"
index de6838b..6437b41 100644 (file)
@@ -187,6 +187,10 @@ void         _ecore_idle_enterer_call(Eo *loop);
 
 void         _ecore_idle_exiter_call(Eo *loop);
 
+
+Eina_Future_Scheduler *_ecore_event_future_scheduler_get(void);
+
+Eina_Bool    _ecore_event_init(void);
 void         _ecore_event_shutdown(void);
 int          _ecore_event_exist(void);
 Ecore_Event *_ecore_event_add(int type,
index aa8ae08..e34deb5 100644 (file)
@@ -1,4 +1,5 @@
 import efl_types;
+import eina_types;
 
 struct Efl.Loop.Arguments {
    [[EFL loop arguments data structure]]
@@ -69,6 +70,18 @@ class Efl.Loop (Efl.Object)
             @in exit_code: ubyte; [[Returned value by begin()]]
          }
       }
+      @property future_scheduler {
+         [[Gets the Eina_Future_Scheduler for a given mainloop.
+
+           The Eina_Future_Scheduler returned by this function
+           should be used for creating promises (eina_promise_new())
+           so then can properly schedule resolve/reject events.
+         ]]
+         get {}
+         values {
+             scheduler: ptr(Eina.Future.Scheduler); [[The scheduler.]]
+         }
+      }
       job {
          [[Will execute that promise in the near future.]]
          params {
index ee65911..76b6d0a 100644 (file)
@@ -273,6 +273,7 @@ extern "C" {
 #include <eina_freeq.h>
 #include <eina_slstr.h>
 #include <eina_debug.h>
+#include <eina_promise.h>
 
 #undef EAPI
 #define EAPI
index 3617715..38865eb 100644 (file)
@@ -159,6 +159,7 @@ EAPI Eina_Inlist *_eina_tracking = NULL;
    S(file);
    S(safepointer);
    S(slstr);
+   S(promise);
 #undef S
 
 struct eina_desc_setup
@@ -205,6 +206,7 @@ static const struct eina_desc_setup _eina_desc_setup[] = {
    S(file),
    S(safepointer),
    S(slstr),
+   S(promise),
 #undef S
 };
 static const size_t _eina_desc_setup_len = sizeof(_eina_desc_setup) /
diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c
new file mode 100644 (file)
index 0000000..596149f
--- /dev/null
@@ -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 (file)
index 0000000..43aa90a
--- /dev/null
@@ -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 (file)
index 0000000..31375b5
--- /dev/null
@@ -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
index 40fbbf2..83be600 100644 (file)
@@ -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]]
+   }
+};