efl/src/lib/ecore/efl_promise.c

1424 lines
33 KiB
C

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <Ecore.h>
#include "ecore_private.h"
typedef struct _Efl_Promise_Data Efl_Promise_Data;
typedef struct _Efl_Promise_Msg Efl_Promise_Msg;
struct _Efl_Promise_Msg
{
EINA_REFCOUNT;
void *value;
Eina_Free_Cb free_cb;
Eina_Error error;
};
struct _Efl_Promise_Data
{
Eo *loop;
Efl_Loop_Data *loop_data;
Efl_Promise *promise;
Efl_Promise_Msg *message;
Eina_List *futures;
unsigned char propagating;
struct {
Eina_Bool future : 1;
Eina_Bool future_triggered : 1;
Eina_Bool progress : 1;
Eina_Bool progress_triggered : 1;
} set;
Eina_Bool optional : 1;
Eina_Bool propagated : 1;
Eina_Bool nodelay : 1;
};
static void
_efl_promise_msg_free(Efl_Promise_Msg *msg)
{
if (!msg) return ;
if (msg->free_cb)
msg->free_cb(msg->value);
free(msg);
}
#define EFL_LOOP_FUTURE_CLASS efl_loop_future_class_get()
static const Efl_Class *efl_loop_future_class_get(void);
typedef struct _Efl_Loop_Future_Data Efl_Loop_Future_Data;
typedef struct _Efl_Loop_Future_Callback Efl_Loop_Future_Callback;
struct _Efl_Loop_Future_Callback
{
EINA_INLIST;
Efl_Event_Cb success;
Efl_Event_Cb failure;
Efl_Event_Cb progress;
Efl_Promise *next;
const void *data;
};
struct _Efl_Loop_Future_Data
{
Eina_Inlist *callbacks;
Efl_Future *self;
Efl_Loop *loop;
Efl_Promise_Msg *message;
Efl_Promise_Data *promise;
#ifndef NDEBUG
int wref;
#endif
unsigned char propagating;
Eina_Bool fulfilled : 1;
Eina_Bool death : 1;
Eina_Bool delayed : 1;
Eina_Bool optional : 1;
};
static void
_efl_loop_future_success(Efl_Event *ev, Efl_Loop_Future_Data *pd, void *value)
{
Efl_Loop_Future_Callback *cb;
Efl_Future_Event_Success chain_success;
ev->info = &chain_success;
ev->desc = EFL_FUTURE_EVENT_SUCCESS;
chain_success.value = value;
pd->propagating++;
// This is done on purpose, we are using cb as a reminder to the head of the list
while ((cb = (void*) pd->callbacks))
{
// Remove callback early to avoid double execution while
// doing recursive call
pd->callbacks = eina_inlist_remove(pd->callbacks, pd->callbacks);
if (cb->next)
{
chain_success.next = cb->next;
if (cb->success) cb->success((void*) cb->data, ev);
}
else
{
chain_success.next = NULL;
chain_success.value = pd->message;
if (cb->success) cb->success((void*) cb->data, ev);
chain_success.value = value;
}
free(cb);
}
pd->propagating--;
}
static void
_efl_loop_future_failure(Efl_Event *ev, Efl_Loop_Future_Data *pd, Eina_Error error)
{
Efl_Loop_Future_Callback *cb;
Efl_Future_Event_Failure chain_fail;
ev->info = &chain_fail;
ev->desc = EFL_FUTURE_EVENT_FAILURE;
chain_fail.error = error;
pd->propagating++;
// This is done on purpose, we are using cb as a reminder to the head of the list
while ((cb = (void*) pd->callbacks))
{
// Remove callback early to avoid double execution while
// doing recursive call
pd->callbacks = eina_inlist_remove(pd->callbacks, pd->callbacks);
chain_fail.next = cb->next;
if (cb->failure) cb->failure((void*) cb->data, ev);
free(cb);
}
pd->propagating--;
}
static void
_efl_loop_future_propagate_force(Eo *obj, Efl_Loop_Future_Data *pd)
{
Efl_Event ev;
ev.object = obj;
if (pd->promise->message->error == 0)
{
_efl_loop_future_success(&ev, pd, pd->message->value);
}
else
{
_efl_loop_future_failure(&ev, pd, pd->message->error);
}
}
static void
_efl_loop_future_propagate(Eo *obj, Efl_Loop_Future_Data *pd)
{
Efl_Event ev;
Eina_Bool cancel;
ev.object = obj;
cancel = pd->fulfilled && !pd->message;
// This has to be done early on to avoid recursive success/failure to
// bypass the fulfilled check.
pd->fulfilled = EINA_TRUE;
if (cancel)
{
_efl_loop_future_failure(&ev, pd, EINA_ERROR_FUTURE_CANCEL);
}
else if (pd->message->error == 0)
{
_efl_loop_future_success(&ev, pd, pd->message->value);
}
else
{
_efl_loop_future_failure(&ev, pd, pd->message->error);
}
if (!pd->delayed)
{
pd->delayed = EINA_TRUE;
efl_unref(obj);
}
}
static void
_efl_loop_future_fulfilling(Eo *obj, Efl_Loop_Future_Data *pd)
{
if (pd->fulfilled)
{
_efl_loop_future_propagate(obj, pd);
}
if (!pd->death)
{
pd->death = EINA_TRUE;
efl_del(obj);
}
}
static void
_efl_loop_future_prepare_events(Efl_Loop_Future_Data *pd, Eina_Bool progress, Eina_Bool optional)
{
if (!pd->promise) return ;
if (pd->optional)
{
pd->optional = EINA_FALSE;
ecore_loop_future_unregister(efl_provider_find(pd->self, EFL_LOOP_CLASS), pd->self);
}
if (!optional)
{
pd->promise->set.future = EINA_TRUE;
if (progress)
pd->promise->set.progress = EINA_TRUE;
}
}
static Efl_Loop_Future_Callback *
_efl_loop_future_then_internal(Efl_Loop_Future_Data *pd,
Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
{
Efl_Loop_Future_Callback *cb;
cb = calloc(1, sizeof (Efl_Loop_Future_Callback));
if (!cb) return NULL;
cb->success = success;
cb->failure = failure;
cb->progress = progress;
cb->data = data;
pd->callbacks = eina_inlist_append(pd->callbacks, EINA_INLIST_GET(cb));
return cb;
}
static Efl_Future *
_efl_loop_future_then(Eo *obj, Efl_Loop_Future_Data *pd,
Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
{
Efl_Loop_Future_Callback *cb;
Efl_Promise_Data *epd;
Efl_Future *f;
cb = _efl_loop_future_then_internal(pd, success, failure, progress, data);
if (!cb) return NULL;
efl_ref(obj);
cb->next = efl_add(EFL_PROMISE_CLASS, obj);
epd = efl_data_scope_get(cb->next, EFL_PROMISE_CLASS);
epd->optional = EINA_TRUE;
f = efl_promise_future_get(cb->next);
_efl_loop_future_prepare_events(pd, !!progress, EINA_FALSE);
_efl_loop_future_fulfilling(obj, pd);
efl_unref(obj);
return f;
}
static Eina_Bool
_efl_loop_future_internal_then(Efl_Future *f,
Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
{
Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS);
Efl_Loop_Future_Callback *cb;
cb = _efl_loop_future_then_internal(pd, success, failure, progress, data);
if (!cb) return EINA_FALSE;
efl_ref(f);
_efl_loop_future_prepare_events(pd, !!progress, EINA_TRUE);
_efl_loop_future_fulfilling(f, pd);
efl_unref(f);
return EINA_TRUE;
}
static void
_efl_loop_future_disconnect(Eo *obj, Efl_Loop_Future_Data *pd)
{
Eo *promise;
if (!pd->promise) return ;
promise = efl_ref(pd->promise->promise);
// Disconnect from the promise
pd->promise->futures = eina_list_remove(pd->promise->futures, pd);
// Notify that there is no more future
if (!pd->promise->futures &&
!pd->promise->message)
{
efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_NONE, NULL);
}
// Unreference after propagating the failure
efl_data_xunref(pd->promise->promise, pd->promise, obj);
pd->promise = NULL;
efl_unref(promise);
}
static void
_efl_loop_future_cancel(Eo *obj, Efl_Loop_Future_Data *pd)
{
// We do allow for calling cancel during the propagation phase
// as the other proper fix is to wype out all future reference before
// starting propagating things.
if (pd->propagating ||
(pd->promise && pd->promise->propagating))
{
efl_ref(obj);
_efl_loop_future_propagate_force(obj, pd);
goto disconnect;
}
// Check state
if (pd->fulfilled)
{
ERR("Triggering cancel on an already fulfilled Efl.Future.");
return;
}
#ifndef NDEBUG
if (!pd->wref)
{
WRN("Calling cancel should be only done on a weak reference. Look at efl_future_use.");
}
#endif
pd->fulfilled = EINA_TRUE;
// Trigger failure
efl_ref(obj);
_efl_loop_future_propagate(obj, pd);
_efl_loop_future_disconnect(obj, pd);
disconnect:
efl_unref(obj);
}
static void
_efl_loop_future_intercept(Eo *obj)
{
Efl_Loop_Future_Data *pd;
// Just delay object death
efl_del_intercept_set(obj, NULL);
// Trigger events now
pd = efl_data_scope_get(obj, EFL_LOOP_FUTURE_CLASS);
if (!pd->promise) return ;
if (pd->promise->set.future && !pd->promise->set.future_triggered)
{
efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_SET, obj);
pd->promise->set.future_triggered = EINA_TRUE;
}
if (pd->promise->set.progress && !pd->promise->set.progress_triggered)
{
efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, obj);
pd->promise->set.progress_triggered = EINA_TRUE;
}
}
static Eo *
_efl_loop_future_efl_object_constructor(Eo *obj, Efl_Loop_Future_Data *pd)
{
obj = efl_constructor(efl_super(obj, EFL_LOOP_FUTURE_CLASS));
pd->loop = efl_ref(efl_provider_find(obj, EFL_LOOP_CLASS));
pd->self = obj;
pd->optional = EINA_TRUE;
ecore_loop_future_register(efl_provider_find(obj, EFL_LOOP_CLASS), obj);
efl_del_intercept_set(obj, _efl_loop_future_intercept);
return obj;
}
static void
_efl_loop_future_efl_object_destructor(Eo *obj, Efl_Loop_Future_Data *pd)
{
Eo *promise = NULL;
if (!pd->fulfilled)
{
ERR("Lost reference to a future without fulfilling it. Forcefully cancelling it.");
_efl_loop_future_propagate(obj, pd);
}
#ifndef NDEBUG
else if (pd->callbacks)
{
ERR("Found referenced callbacks while destroying the future.");
_efl_loop_future_propagate(obj, pd);
}
#endif
// Cleanup message if needed
if (pd->message)
{
EINA_REFCOUNT_UNREF(pd->message)
_efl_promise_msg_free(pd->message);
pd->message = NULL;
}
// Stop the main loop handler that would destroy this optional future
if (pd->optional)
{
pd->optional = EINA_FALSE;
ecore_loop_future_unregister(efl_provider_find(pd->self, EFL_LOOP_CLASS), pd->self);
}
if (pd->promise) promise = efl_ref(pd->promise->promise);
efl_destructor(efl_super(obj, EFL_LOOP_FUTURE_CLASS));
// Disconnect from the promise
_efl_loop_future_disconnect(obj, pd);
efl_unref(promise);
efl_unref(pd->loop);
}
#ifndef NDEBUG
static void
_efl_future_wref_add(Eo *obj, Efl_Loop_Future_Data *pd, Eo **wref)
{
efl_wref_add(efl_super(obj, EFL_LOOP_FUTURE_CLASS), wref);
pd->wref++;
}
static void
_efl_future_wref_del(Eo *obj, Efl_Loop_Future_Data *pd, Eo **wref)
{
efl_wref_del(efl_super(obj, EFL_LOOP_FUTURE_CLASS), wref);
pd->wref--;
}
#endif
static Efl_Object *
_efl_loop_future_efl_object_provider_find(Eo *obj EINA_UNUSED, Efl_Loop_Future_Data *pd, const Efl_Object *klass)
{
Efl_Object *r = NULL;
if (pd->loop) r = efl_provider_find(pd->loop, klass);
if (r) return r;
return efl_provider_find(efl_super(obj, EFL_LOOP_FUTURE_CLASS), klass);
}
static Eina_Bool
_efl_loop_future_class_initializer(Efl_Class *klass)
{
EFL_OPS_DEFINE(ops,
#ifndef NDEBUG
EFL_OBJECT_OP_FUNC(efl_wref_add, _efl_future_wref_add),
EFL_OBJECT_OP_FUNC(efl_wref_del, _efl_future_wref_del),
#endif
EFL_OBJECT_OP_FUNC(efl_provider_find, _efl_loop_future_efl_object_provider_find),
EFL_OBJECT_OP_FUNC(efl_future_then, _efl_loop_future_then),
EFL_OBJECT_OP_FUNC(efl_future_cancel, _efl_loop_future_cancel),
EFL_OBJECT_OP_FUNC(efl_constructor, _efl_loop_future_efl_object_constructor),
EFL_OBJECT_OP_FUNC(efl_destructor, _efl_loop_future_efl_object_destructor));
return efl_class_functions_set(klass, &ops, NULL);
};
static const Efl_Class_Description _efl_loop_future_class_desc = {
EO_VERSION,
"Efl.Future",
EFL_CLASS_TYPE_REGULAR,
sizeof (Efl_Loop_Future_Data),
_efl_loop_future_class_initializer,
NULL,
NULL
};
EFL_DEFINE_CLASS(efl_loop_future_class_get, &_efl_loop_future_class_desc, EFL_FUTURE_CLASS, NULL);
static Efl_Future *
_efl_promise_future_get(Eo *obj, Efl_Promise_Data *pd EINA_UNUSED)
{
Efl_Future *f;
Efl_Loop_Future_Data *fd;
// Build a new future, attach it and return it
f = efl_add(EFL_LOOP_FUTURE_CLASS, obj);
if (!f) return NULL;
fd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS);
fd->promise = efl_data_xref(obj, EFL_PROMISE_CLASS, f);
fd->promise->futures = eina_list_append(fd->promise->futures, fd);
// The promise has already been fulfilled, prepare the propagation
if (fd->promise->message &&
fd->promise->propagated)
{
fd->message = fd->promise->message;
EINA_REFCOUNT_REF(fd->message);
_efl_loop_future_propagate(f, fd);
}
return f;
}
static Efl_Promise_Msg *
_efl_promise_message_new(Efl_Promise_Data *pd)
{
Efl_Promise_Msg *message;
message = calloc(1, sizeof (Efl_Promise_Msg));
if (!message) return NULL;
EINA_REFCOUNT_INIT(message);
pd->message = message;
return message;
}
static void
_efl_promise_propagate(Eo *obj, Efl_Promise_Data *pd)
{
Efl_Loop_Future_Data *f;
Eina_List *l, *ln;
pd->propagated = EINA_TRUE;
// By triggering this message, we are likely going to kill all future
// And a user of the promise may want to attach an event handler on the promise
// and destroy it, so delay that to after the loop is done.
efl_ref(obj);
// Send it to all futures
pd->propagating++;
EINA_LIST_FOREACH_SAFE(pd->futures, l, ln, f)
{
EINA_REFCOUNT_REF(pd->message);
f->message = pd->message;
// Trigger the callback
_efl_loop_future_propagate(f->self, f);
}
pd->propagating--;
// Now, we may die.
efl_unref(obj);
}
void
ecore_loop_promise_fulfill(Eo *obj)
{
Efl_Promise_Data *pd = efl_data_scope_get(obj, EFL_PROMISE_CLASS);
_efl_promise_propagate(obj, pd);
}
static void
_efl_promise_value_set(Eo *obj, Efl_Promise_Data *pd, void *v, Eina_Free_Cb free_cb)
{
Efl_Promise_Msg *message;
if (pd->message)
{
ERR("This promise has already been fulfilled. You can can't set a value twice nor can you set a value after it has been cancelled.");
return ;
}
// Create a refcounted structure where refcount == number of future + one
message = _efl_promise_message_new(pd);
if (!message) return ;
message->value = v;
message->free_cb = free_cb;
EINA_REFCOUNT_INIT(message);
pd->message = message;
if (pd->nodelay)
_efl_promise_propagate(obj, pd);
else
ecore_loop_promise_register(efl_provider_find(obj, EFL_LOOP_CLASS), obj);
}
static void
_efl_promise_failed_set(Eo *obj, Efl_Promise_Data *pd, Eina_Error err)
{
Efl_Promise_Msg *message;
if (pd->message)
{
ERR("This promise has already been fulfilled. You can can't set a value twice nor can you set a value after it has been cancelled.");
return ;
}
// Create a refcounted structure where refcount == number of future + one
message = _efl_promise_message_new(pd);
if (!message) return ;
message->error = err;
EINA_REFCOUNT_INIT(message);
pd->message = message;
if (pd->nodelay)
_efl_promise_propagate(obj, pd);
else
ecore_loop_promise_register(efl_provider_find(obj, EFL_LOOP_CLASS), obj);
}
static void
_efl_promise_progress_set(Eo *obj, Efl_Promise_Data *pd, const void *p)
{
Efl_Loop_Future_Data *f;
Eina_List *l, *ln;
Efl_Future_Event_Progress chain_progress;
Efl_Event ev;
chain_progress.progress = p;
ev.object = obj;
ev.info = &chain_progress;
ev.desc = EFL_FUTURE_EVENT_PROGRESS;
EINA_LIST_FOREACH_SAFE(pd->futures, l, ln, f)
{
Efl_Loop_Future_Callback *cb;
EINA_INLIST_FOREACH(f->callbacks, cb)
{
if (cb->next)
{
chain_progress.next = cb->next;
if (cb->progress) cb->progress((void*) cb->data, &ev);
}
}
}
}
static void
_efl_promise_loop_clear(Eo *obj, Efl_Promise_Data *pd)
{
pd->nodelay = EINA_TRUE;
// Unref refcounted structure
if (!pd->message && pd->futures)
{
if (!pd->optional) ERR("This promise has not been fulfilled. Forcefully cancelling %p.", obj);
efl_promise_failed_set(obj, EINA_ERROR_FUTURE_CANCEL);
}
if (pd->message &&
!pd->propagated)
{
ecore_loop_promise_unregister(pd->loop, obj);
_efl_promise_propagate(obj, pd);
}
if (pd->message)
{
EINA_REFCOUNT_UNREF(pd->message)
_efl_promise_msg_free(pd->message);
pd->message = NULL;
}
}
static void
_efl_promise_efl_object_parent_set(Eo *obj, Efl_Promise_Data *pd, Efl_Object *parent)
{
if (!parent) _efl_promise_loop_clear(obj, pd);
efl_parent_set(efl_super(obj, EFL_PROMISE_CLASS), parent);
pd->loop = efl_provider_find(obj, EFL_LOOP_CLASS);
pd->loop_data = efl_data_scope_get(pd->loop, EFL_LOOP_CLASS);
}
static Efl_Object *
_efl_promise_efl_object_constructor(Eo *obj, Efl_Promise_Data *pd)
{
pd->promise = obj;
return efl_constructor(efl_super(obj, EFL_PROMISE_CLASS));
}
static void
_efl_promise_efl_object_destructor(Eo *obj, Efl_Promise_Data *pd)
{
_efl_promise_loop_clear(obj, pd);
efl_destructor(efl_super(obj, EFL_PROMISE_CLASS));
}
static void
_efl_promise_connect_then(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Success *success = ev->info;
Efl_Promise_Data *pd = data;
// This is a trick due to the fact we are using internal function call to register this functions
Efl_Promise_Msg *d = success->value;
EINA_REFCOUNT_REF(d);
pd->message = d;
_efl_promise_propagate(pd->promise, pd);
efl_unref(pd->promise);
}
static void
_efl_promise_connect_fail(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Failure *fail = ev->info;
Efl_Promise_Data *pd = data;
efl_promise_failed_set(pd->promise, fail->error);
efl_unref(pd->promise);
}
static Eina_Bool
_efl_promise_connect(Eo *obj, Efl_Promise_Data *pd, Efl_Future *f)
{
// We have to keep a reference on the promise to avoid it dying before the future
efl_ref(obj);
return _efl_loop_future_internal_then(f, _efl_promise_connect_then, _efl_promise_connect_fail, NULL, pd);
}
typedef struct _Efl_Promise_Composite Efl_Promise_All;
typedef struct _Efl_Future_All Efl_Future_All;
typedef struct _Efl_Accessor_All Efl_Accessor_All;
typedef struct _Efl_Promise_Composite Efl_Promise_Race;
struct _Efl_Accessor_All
{
Eina_Accessor accessor;
Efl_Promise_All *all;
Efl_Promise *promise;
};
struct _Efl_Future_All
{
Efl_Future *f;
Efl_Promise_Msg *d;
};
struct _Efl_Promise_Composite
{
Eina_Array members;
Efl_Promise *promise;
Efl_Future *(*future_get)(void *item);
Eina_Error error;
Eina_Bool failed : 1;
Eina_Bool progress_triggered : 1;
Eina_Bool future_triggered : 1;
Eina_Bool done : 1;
Eina_Bool building : 1;
};
static Efl_Future *
_efl_promise_all_future_get(void *item)
{
Efl_Future_All *fa = item;
return fa->f;
}
static Efl_Future *
_efl_promise_race_future_get(void *item)
{
return item;
}
static void
_efl_promise_all_free(Efl_Promise_All *all)
{
Efl_Future_All *fa;
Eina_Array_Iterator iterator;
unsigned int i;
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
{
if (fa->d)
{
EINA_REFCOUNT_UNREF(fa->d)
_efl_promise_msg_free(fa->d);
fa->d = NULL;
}
}
efl_del(all->promise);
all->promise = NULL;
}
static void
_efl_promise_all_die(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_All *all = data;
Efl_Future_All *fa;
while ((fa = eina_array_pop(&all->members)))
{
if (fa->d)
{
EINA_REFCOUNT_UNREF(fa->d)
_efl_promise_msg_free(fa->d);
}
assert(fa->f == NULL);
free(fa);
}
eina_array_flush(&all->members);
free(all);
}
static void
_efl_promise_race_die(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_All *all = data;
eina_array_flush(&all->members);
free(all);
}
static void
_efl_future_set(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_All *all = data;
void *item;
Eina_Array_Iterator iterator;
unsigned int i;
if (all->future_triggered) return ;
all->future_triggered = EINA_TRUE;
// Propagate set on demand
EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator)
{
Efl_Future *f = all->future_get(item);
if (f)
{
Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS);
if (!pd->promise->set.future && !pd->promise->set.future_triggered)
{
efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_SET, f);
pd->promise->set.future_triggered = EINA_TRUE;
pd->promise->set.future = EINA_TRUE;
}
}
}
}
static void
_efl_future_progress_set(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_All *all = data;
void *item;
Eina_Array_Iterator iterator;
unsigned int i;
if (all->progress_triggered) return ;
all->progress_triggered = 1;
// Propagate progress set
EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator)
{
Efl_Future *f = all->future_get(item);
if (f)
{
Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS);
if (!pd->promise->set.progress && !pd->promise->set.progress_triggered)
{
efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, f);
pd->promise->set.progress_triggered = EINA_TRUE;
pd->promise->set.progress = EINA_TRUE;
}
}
}
}
static void
_efl_all_future_none(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_All *all = data;
Efl_Future_All *fa;
Eina_Array_Iterator iterator;
unsigned int i;
if (all->failed) return ;
all->failed = EINA_TRUE;
// Trigger cancel on all future
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
{
if (!fa->d && fa->f) efl_future_cancel(fa->f);
}
// No one is listening to this promise anyway
_efl_promise_all_free(all);
}
static void
_efl_race_future_none(void *data, const Efl_Event *ev EINA_UNUSED)
{
Efl_Promise_Race *all = data;
Efl_Future *f;
Eina_Array_Iterator iterator;
unsigned int i;
if (all->failed) return ;
all->failed = EINA_TRUE;
// Trigger cancel on all future
EINA_ARRAY_ITER_NEXT(&all->members, i, f, iterator)
{
efl_future_cancel(f);
}
// No one is listening to this promise anyway
_efl_promise_all_free(all);
}
static Eina_Bool
_efl_accessor_all_get_at(Efl_Accessor_All *ac, unsigned int pos, void **data)
{
Efl_Future_All *fa;
if (eina_array_count(&ac->all->members) <= pos)
return EINA_FALSE;
fa = eina_array_data_get(&ac->all->members, pos);
if (!fa) return EINA_FALSE;
*data = fa->d->value;
return EINA_TRUE;
}
static Efl_Promise *
_efl_accessor_all_get_container(Efl_Accessor_All *ac)
{
return ac->all->promise;
}
static void
_efl_accessor_all_free(Efl_Accessor_All *ac)
{
_efl_promise_all_free(ac->all);
efl_unref(ac->promise);
free(ac);
}
static void
_real_then_all(Efl_Promise_All *all)
{
Efl_Accessor_All *ac;
ac = calloc(1, sizeof (Efl_Accessor_All));
if (!ac) return ; // We do now the promise and all here
EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR);
ac->accessor.version = EINA_ACCESSOR_VERSION;
ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(_efl_accessor_all_get_at);
ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(_efl_accessor_all_get_container);
ac->accessor.free = FUNC_ACCESSOR_FREE(_efl_accessor_all_free);
ac->all = all;
ac->promise = efl_ref(all->promise);
efl_promise_value_set(all->promise, &ac->accessor, EINA_FREE_CB(eina_accessor_free));
}
static void
_then_all(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Success *success = ev->info;
Efl_Promise_All *all = data;
Efl_Future_All *fa;
// This is a trick due to the fact we are using internal function call to register this functions
Efl_Promise_Msg *d = success->value;
Eina_Array_Iterator iterator;
unsigned int i;
Eina_Bool done = EINA_TRUE;
// Only when all value are received can we propagate the success
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
{
if (fa->f == ev->object)
{
fa->d = d;
EINA_REFCOUNT_REF(fa->d);
}
done &= !!fa->d;
}
if (all->building) return ;
if (done)
{
_real_then_all(all);
}
}
static void
_real_fail_all(Efl_Promise_All *all, Eina_Error error)
{
efl_promise_failed_set(all->promise, error);
_efl_promise_all_free(all);
}
static void
_fail_all(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Failure *fail = ev->info;
Efl_Promise_All *all = data;
Efl_Future_All *fa;
Eina_Array_Iterator iterator;
unsigned int i;
if (all->failed) return ;
all->failed = EINA_TRUE;
if (all->building)
{
all->error = fail->error;
return ;
}
efl_ref(all->promise);
// In case of one fail, the entire promise will fail and
// all remaining future will be cancelled
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
{
if (fa->d)
{
EINA_REFCOUNT_UNREF(fa->d)
_efl_promise_msg_free(fa->d);
fa->d = NULL;
}
else
{
efl_future_cancel(fa->f);
}
}
_real_fail_all(all, fail->error);
efl_unref(all->promise);
}
static void
_progress(void *data, const Efl_Event *ev)
{
Efl_Promise_All *all = data;
Efl_Future_Event_Progress *p = ev->info;
void *item;
Efl_Future_All_Progress a;
Eina_Array_Iterator iterator;
unsigned int i;
a.inprogress = ev->object;
a.progress = (Efl_Object *) p->progress;
EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator)
{
Efl_Future *f = all->future_get(item);
if (f == a.inprogress) break ;
}
a.index = i;
efl_promise_progress_set(all->promise, &a);
}
EFL_CALLBACKS_ARRAY_DEFINE(efl_all_callbacks,
{ EFL_PROMISE_EVENT_FUTURE_SET, _efl_future_set },
{ EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, _efl_future_progress_set },
{ EFL_PROMISE_EVENT_FUTURE_NONE, _efl_all_future_none },
{ EFL_EVENT_DEL, _efl_promise_all_die });
static inline Efl_Promise_All *
_efl_future_all_new(Eo *provider)
{
Efl_Promise_All *all;
Eo *loop;
loop = efl_provider_find(provider, EFL_LOOP_CLASS);
if (!loop) return NULL;
all = calloc(1, sizeof (Efl_Promise_All));
if (!all) return NULL;
eina_array_step_set(&all->members, sizeof (Eina_Array), 8);
all->future_get = _efl_promise_all_future_get;
all->promise = efl_add(EFL_PROMISE_CLASS, loop);
if (!all->promise) goto on_error;
all->building = EINA_TRUE;
return all;
on_error:
free(all);
return NULL;
}
static inline Efl_Future *
_efl_future_all_done(Efl_Promise_All *all)
{
Efl_Future *r;
Efl_Future_All *fa;
Eina_Array_Iterator iterator;
unsigned int i;
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
_efl_loop_future_internal_then(fa->f, _then_all, _fail_all, _progress, all);
efl_event_callback_array_add(all->promise, efl_all_callbacks(), all);
r = efl_promise_future_get(all->promise);
all->building = EINA_FALSE;
if (all->failed)
{
_real_fail_all(all, all->error);
}
else
{
Eina_Bool done = EINA_TRUE;
EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
done &= !!fa->d;
if (done) _real_then_all(all);
}
return r;
}
static Eina_Bool
_efl_future_all_append(Efl_Promise_All *all, Efl_Future *fn)
{
Efl_Future_All *fa;
fa = calloc(1, sizeof (Efl_Future_All));
if (!fa) return EINA_FALSE;
efl_future_use(&fa->f, fn);
eina_array_push(&all->members, fa);
return EINA_TRUE;
}
EAPI Efl_Future *
efl_future_all_internal(Efl_Future *f1, ...)
{
Efl_Promise_All *all;
Efl_Future *fn;
va_list args;
if (!f1) return NULL;
all = _efl_future_all_new(f1);
if (!all) return NULL;
if (!_efl_future_all_append(all, f1))
goto on_error;
va_start(args, f1);
while ((fn = va_arg(args, Efl_Future *)))
{
if (!_efl_future_all_append(all, fn))
{
va_end(args);
goto on_error;
}
}
va_end(args);
return _efl_future_all_done(all);
on_error:
_efl_promise_all_die(all, NULL);
return NULL;
}
EAPI Efl_Future *
efl_future_iterator_all(Eina_Iterator *it)
{
Efl_Promise_All *all = NULL;
Efl_Future *fn;
if (!it) return NULL;
EINA_ITERATOR_FOREACH(it, fn)
{
if (!all) all = _efl_future_all_new(fn);
if (!all) goto on_error;
if (!_efl_future_all_append(all, fn))
goto on_error;
}
eina_iterator_free(it);
return _efl_future_all_done(all);
on_error:
eina_iterator_free(it);
if (all) _efl_promise_all_die(all, NULL);
return NULL;
}
static void
_then_race(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Success *ev_success = ev->info;
Efl_Promise_Race *race = data;
// This is a trick due to the fact we are using internal function call to register this functions
Efl_Promise_Msg *d = ev_success->value;
Efl_Future *f;
Eina_Array_Iterator iterator;
unsigned int i;
if (race->done) return ;
race->done = EINA_TRUE;
efl_ref(race->promise);
EINA_ARRAY_ITER_NEXT(&race->members, i, f, iterator)
{
// To avoid double cancel/success
eina_array_data_set(&race->members, i, NULL);
if (f == ev->object)
{
Efl_Future_Race_Success *success = calloc(1, sizeof (Efl_Future_Race_Success));
if (!success) continue ;
success->winner = f;
success->value = d->value;
success->index = i;
efl_promise_value_set(race->promise, success, free);
}
else
{
efl_future_cancel(f);
}
}
_efl_promise_all_free(race);
efl_unref(race->promise);
}
static void
_fail_race(void *data, const Efl_Event *ev)
{
Efl_Future_Event_Failure *fail = ev->info;
Efl_Promise_Race *race = data;
Efl_Future *f;
Eina_Array_Iterator iterator;
unsigned int i;
if (race->done) return ;
race->done = EINA_TRUE;
efl_ref(race->promise);
EINA_ARRAY_ITER_NEXT(&race->members, i, f, iterator)
{
// To avoid double cancel/success
eina_array_data_set(&race->members, i, NULL);
if (f != ev->object)
{
efl_future_cancel(f);
}
}
efl_promise_failed_set(race->promise, fail->error);
_efl_promise_all_free(race);
efl_unref(race->promise);
}
EFL_CALLBACKS_ARRAY_DEFINE(efl_race_callbacks,
{ EFL_PROMISE_EVENT_FUTURE_SET, _efl_future_set },
{ EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, _efl_future_progress_set },
{ EFL_PROMISE_EVENT_FUTURE_NONE, _efl_race_future_none },
{ EFL_EVENT_DEL, _efl_promise_race_die });
static Efl_Promise_Race *
_efl_future_race_new(Eo *provider)
{
Efl_Promise_Race *race;
Eo *loop;
loop = efl_provider_find(provider, EFL_LOOP_CLASS);
if (!loop) return NULL;
race = calloc(1, sizeof (Efl_Promise_Race));
if (!race) return NULL;
eina_array_step_set(&race->members, sizeof (Eina_Array), 8);
race->future_get = _efl_promise_race_future_get;
race->promise = efl_add(EFL_PROMISE_CLASS, loop);
if (!race->promise) goto on_error;
return race;
on_error:
free(race);
return NULL;
}
static inline Efl_Future *
_efl_future_race_done(Efl_Promise_Race *race)
{
Efl_Future *r;
Efl_Future *fn;
Eina_Array_Iterator iterator;
unsigned int i;
r = efl_promise_future_get(race->promise);
EINA_ARRAY_ITER_NEXT(&race->members, i, fn, iterator)
_efl_loop_future_internal_then(fn, _then_race, _fail_race, _progress, race);
efl_event_callback_array_add(race->promise, efl_race_callbacks(), race);
return r;
}
EAPI Efl_Future *
efl_future_race_internal(Efl_Future *f1, ...)
{
Efl_Promise_Race *race;
Efl_Future *fn;
va_list args;
if (!f1) return NULL;
race = _efl_future_race_new(f1);
if (!race) return NULL;
eina_array_push(&race->members, f1);
va_start(args, f1);
while ((fn = va_arg(args, Efl_Future *)))
{
eina_array_push(&race->members, fn);
}
return _efl_future_race_done(race);
}
EAPI Efl_Future *
efl_future_iterator_race(Eina_Iterator *it)
{
Efl_Promise_Race *race = NULL;
Efl_Future *fn;
if (!it) return NULL;
EINA_ITERATOR_FOREACH(it, fn)
{
if (!race) race = _efl_future_race_new(fn);
if (!race) goto on_error;
eina_array_push(&race->members, fn);
}
eina_iterator_free(it);
return _efl_future_race_done(race);
on_error:
eina_iterator_free(it);
return NULL;
}
#include "efl_promise.eo.c"