forked from enlightenment/efl
Efl_Object: Add integration with Eina_Future.
This commit adds the EO support for the new future infra. From now on there's no need to efl_future_link()/efl_future_unlink() object and futures since the new API already handles that internally.
This commit is contained in:
parent
d30c0d4f03
commit
0dd2c1a530
235
src/lib/eo/Eo.h
235
src/lib/eo/Eo.h
|
@ -340,6 +340,239 @@ EOAPI Eina_Bool efl_event_callback_legacy_call(Eo *obj, const Efl_Event_Descript
|
|||
*/
|
||||
EOAPI Eina_Bool efl_future_link(Eo *obj, Efl_Future *link);
|
||||
|
||||
|
||||
/**
|
||||
* @struct _Efl_Future_Cb_Desc
|
||||
*
|
||||
* A struct with callbacks to be used by efl_future_cb_from_desc() and efl_future_chain_from_array()
|
||||
*
|
||||
* @see efl_future_cb_from_desc()
|
||||
* @see efl_future_chain_from_array()
|
||||
*/
|
||||
typedef struct _Efl_Future_Cb_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.
|
||||
*
|
||||
* @note This function is always called from a safe context (main loop or some platform defined safe context).
|
||||
*
|
||||
* @param o The object used to create the link in efl_future_cb_from_desc() or efl_future_chain_from_array().
|
||||
* @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).
|
||||
*/
|
||||
Eina_Value (*success)(Eo *o, const Eina_Value value);
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @note 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.
|
||||
*
|
||||
*
|
||||
* @param o The object used to create the link in efl_future_cb_from_desc() or efl_future_chain_from_array().
|
||||
* @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).
|
||||
*/
|
||||
Eina_Value (*error)(Eo *o, Eina_Error 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.
|
||||
*
|
||||
* @note 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.
|
||||
*
|
||||
* @param o The object used to create the link in efl_future_cb_from_desc() or efl_future_chain_from_array().
|
||||
* @param dead_future The future that was freed.
|
||||
*/
|
||||
void (*free)(Eo *o, const Eina_Future *dead_future);
|
||||
/**
|
||||
* 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;
|
||||
/**
|
||||
* This is used by Eo to cancel a pending futures in case
|
||||
* an Eo object is deleted. It can be @c NULL.
|
||||
*/
|
||||
Eina_Future **storage;
|
||||
} Efl_Future_Cb_Desc;
|
||||
|
||||
/**
|
||||
* Creates an Eina_Future_Desc for an EO object.
|
||||
*
|
||||
* This function creates an Eina_Future_Desc based on an Efl_Future_Cb_Desc.
|
||||
* The main purpose of this function is create a "link" between the future
|
||||
* and the object. In case the object is deleted before the future is resolved/rejected,
|
||||
* the object destructor will cancel the future.
|
||||
*
|
||||
* @note In case context info are needed for the #Efl_Future_Desc callbacks efl_key_data_set()
|
||||
* can be used.
|
||||
*
|
||||
* The example below shows a file download using an Eo object, if the download
|
||||
* lasts more than 30 seconds the Eo object will be deleted, causing the
|
||||
* future to also be deleted.
|
||||
* Usually this would be done with an eina_future_race() of the download promise and a timeout promise,
|
||||
* however we provide the following example to illustrate efl_key_data_set() usage.
|
||||
*
|
||||
* @code
|
||||
*
|
||||
* static Eina_Bool
|
||||
* _timeout(void *data)
|
||||
* {
|
||||
* Eo *downloader = data;
|
||||
* //In case the download is not completed yet.
|
||||
* //Delete the downloader (which in cancel the file download and the future)
|
||||
* efl_key_data_set(downloader, "timer", NULL);
|
||||
* efl_unref(downloader);
|
||||
* return EINA_FALSE;
|
||||
* }
|
||||
*
|
||||
* static Eina_Value
|
||||
* _file_ok(Eo *o EINA_UNUSED, const Eina_Value value)
|
||||
* {
|
||||
* const char *data;
|
||||
* //There's no need to check the value type since EO infra already did that for us
|
||||
* eina_value_get(&value, &data);
|
||||
* //Deliver the data to the user
|
||||
* data_deliver(data);
|
||||
* return v;
|
||||
* }
|
||||
*
|
||||
* static Eina_Value
|
||||
* _file_err(Eo *o EINA_UNUSED, Eina_Error error)
|
||||
* {
|
||||
* //In case the downloader is deleted before the future is resolved, the future will be canceled thus this callback will be called.
|
||||
* fprintf(stderr, "Could not download the file. Reason: %s\n", eina_error_msg_get(error));
|
||||
* return EINA_VALUE_EMPTY;
|
||||
* }
|
||||
*
|
||||
* static void
|
||||
* _downlader_free(Eo *o, const Eina_Future *dead_future EINA_UNUSED)
|
||||
* {
|
||||
* Ecore_Timer *t = efl_key_data_get(o, "timer");
|
||||
* //The download was finished before the timer expired. Cancel it...
|
||||
* if (t)
|
||||
* {
|
||||
* ecore_timer_del(t);
|
||||
* efl_unref(o); //Delete the object
|
||||
* } //else - In this case the future was canceled due efl_unref() in _timeout - No need to call efl_unref()
|
||||
* }
|
||||
*
|
||||
* void download_file(const char *file)
|
||||
* {
|
||||
* //This could be rewritten using eina_future_race()
|
||||
* Eo *downloader = efl_add(MY_DOWNLOADER_CLASS, NULL);
|
||||
* Eina_Future *f = downloader_download_file(downloader, file);
|
||||
* timer = ecore_timer_add(30, _timeout, downloader);
|
||||
* //Usually this would be done with an eina_future_race() of the download promise and a timeout promise,
|
||||
* //however we provide the following example to illustrate efl_key_data_set() usage.
|
||||
* efl_key_data_set(downloader, "timer", timer);
|
||||
* eina_future_then_from_desc(f, efl_future_cb(.success = _file_ok, .error = _file_err, .success_type = EINA_VALUE_TYPE_STRING, .free = downloader_free));
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* @param obj The object to create the link.
|
||||
* @param desc An Efl_Future_Cb_Desc
|
||||
* @return An Eina_Future_Desc to be used by eina_future_then(), eina_future_chain() and friends.
|
||||
* @see efl_future_chain_from_array()
|
||||
* @see efl_future_cb()
|
||||
* @see #Efl_Future_Cb_Desc
|
||||
* @see efl_key_data_set()
|
||||
*/
|
||||
EOAPI Eina_Future_Desc efl_future_cb_from_desc(Eo *obj, const Efl_Future_Cb_Desc desc);
|
||||
|
||||
/**
|
||||
* Syntax suger over efl_future_cb_from_desc()
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* eina_future_then_from_desc(future, efl_future_cb(my_object, .succes = success, .success_type = EINA_VALUE_TYPE_INT));
|
||||
* @endcode
|
||||
*
|
||||
* @see efl_future_cb_from_desc()
|
||||
*/
|
||||
#define efl_future_cb(_eo, ...) efl_future_cb_from_desc(_eo, (Efl_Future_Cb_Desc){__VA_ARGS__})
|
||||
|
||||
/**
|
||||
* Creates an Future chain based on #Efl_Future_Cb_Desc
|
||||
*
|
||||
* This function is an wrapper around efl_future_cb_from_desc() and eina_future_then_from_desc()
|
||||
*
|
||||
* For more information about them, check their documentations.
|
||||
*
|
||||
*
|
||||
* @param obj An EO object to link against the future
|
||||
* @param prev The previous future
|
||||
* @param descs An array of Efl_Future_Cb_Desc
|
||||
* @return An Eina_Future or @c NULL on error.
|
||||
* @note If an error happens the whole future chain will be CANCELED, causing
|
||||
* desc.error to be called passing `ENOMEM` or `EINVAL` and desc.free
|
||||
* to free the @p obj if necessary.
|
||||
*
|
||||
* @see efl_future_chain()
|
||||
* @see efl_future_cb()
|
||||
* @see eina_future_then_from_desc()
|
||||
* @see #Efl_Future_Cb_Desc
|
||||
*/
|
||||
EOAPI Eina_Future *efl_future_chain_from_array(Eo *obj, Eina_Future *prev, const Efl_Future_Cb_Desc descs[]);
|
||||
|
||||
/**
|
||||
* Syntax suger over efl_future_chain_from_array()
|
||||
*
|
||||
* Usage:
|
||||
* @code
|
||||
* Eina_Future *f = efl_future_chain(my_object, prev_future, {}, {});
|
||||
* @endcode
|
||||
*
|
||||
* @see efl_future_chain_from_array()
|
||||
*/
|
||||
#define efl_future_chain(_eo, _prev, ...) efl_future_chain_from_array(_eo, _prev, (Efl_Future_Cb_Desc []){__VA_ARGS__, {NULL, NULL, NULL, NULL, NULL}})
|
||||
|
||||
/**
|
||||
* @addtogroup Eo_Debug_Information Eo's Debug information helper.
|
||||
* @{
|
||||
|
@ -1806,6 +2039,8 @@ efl_replace(Eo **storage, Eo *new_obj)
|
|||
*storage = new_obj;
|
||||
}
|
||||
|
||||
EOAPI extern const Eina_Value_Type *EINA_VALUE_TYPE_OBJECT;
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
|
|
@ -3182,3 +3182,98 @@ eo_objects_iterator_new(void)
|
|||
|
||||
return (Eina_Iterator *)it;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_setup(const Eina_Value_Type *type EINA_UNUSED, void *mem)
|
||||
{
|
||||
Eo **tmem = mem;
|
||||
*tmem = NULL;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_flush(const Eina_Value_Type *type EINA_UNUSED, void *mem)
|
||||
{
|
||||
Eo **tmem = mem;
|
||||
if (*tmem)
|
||||
{
|
||||
efl_unref(*tmem);
|
||||
*tmem = NULL;
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
_eo_value_replace(Eo **dst, Eo * const *src)
|
||||
{
|
||||
if (*src == *dst) return;
|
||||
//ref *src first, since efl_unref(*dst) may trigger *src unref()
|
||||
efl_ref(*src);
|
||||
efl_unref(*dst);
|
||||
*dst = *src;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_vset(const Eina_Value_Type *type EINA_UNUSED, void *mem, va_list args)
|
||||
{
|
||||
Eo **dst = mem;
|
||||
Eo **src = va_arg(args, Eo **);
|
||||
_eo_value_replace(dst, src);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_pset(const Eina_Value_Type *type EINA_UNUSED,
|
||||
void *mem, const void *ptr)
|
||||
{
|
||||
Eo **dst = mem;
|
||||
Eo * const *src = ptr;
|
||||
_eo_value_replace(dst, src);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_pget(const Eina_Value_Type *type EINA_UNUSED,
|
||||
const void *mem, void *ptr)
|
||||
{
|
||||
Eo * const *src = mem;
|
||||
Eo **dst = ptr;
|
||||
*dst = *src;
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
_eo_value_convert_to(const Eina_Value_Type *type EINA_UNUSED, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
|
||||
{
|
||||
Eo * const *eo = type_mem;
|
||||
|
||||
if (convert == EINA_VALUE_TYPE_STRINGSHARE ||
|
||||
convert == EINA_VALUE_TYPE_STRING)
|
||||
{
|
||||
const char *other_mem;
|
||||
char buf[256];
|
||||
snprintf(buf, sizeof(buf), "Object id: %p, class: %s, name: %s",
|
||||
*eo, efl_class_name_get(efl_class_get(*eo)),
|
||||
efl_debug_name_get(*eo));
|
||||
other_mem = buf;
|
||||
return eina_value_type_pset(convert, convert_mem, &other_mem);
|
||||
}
|
||||
return EINA_FALSE;
|
||||
}
|
||||
|
||||
static const Eina_Value_Type _EINA_VALUE_TYPE_OBJECT = {
|
||||
.version = EINA_VALUE_TYPE_VERSION,
|
||||
.value_size = sizeof(Eo *),
|
||||
.name = "Efl_Object",
|
||||
.setup = _eo_value_setup,
|
||||
.flush = _eo_value_flush,
|
||||
.copy = NULL,
|
||||
.compare = NULL,
|
||||
.convert_to = _eo_value_convert_to,
|
||||
.convert_from = NULL,
|
||||
.vset = _eo_value_vset,
|
||||
.pset = _eo_value_pset,
|
||||
.pget = _eo_value_pget
|
||||
};
|
||||
|
||||
EOAPI const Eina_Value_Type *EINA_VALUE_TYPE_OBJECT = &_EINA_VALUE_TYPE_OBJECT;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "Eo.h"
|
||||
#include "eo_ptr_indirection.h"
|
||||
#include "eo_private.h"
|
||||
#include "eina_promise_private.h"
|
||||
|
||||
#define EFL_EVENT_SPECIAL_SKIP 1
|
||||
|
||||
|
@ -45,6 +46,7 @@ typedef struct
|
|||
|
||||
Efl_Event_Callback_Frame *event_frame;
|
||||
Eo_Callback_Description **callbacks;
|
||||
Eina_Inlist *pending_futures;
|
||||
unsigned int callbacks_count;
|
||||
|
||||
unsigned short event_freeze_count;
|
||||
|
@ -79,6 +81,15 @@ typedef struct
|
|||
Eo_Generic_Data_Node_Type d_type;
|
||||
} Eo_Generic_Data_Node;
|
||||
|
||||
typedef struct _Efl_Future_Pending
|
||||
{
|
||||
EINA_INLIST;
|
||||
Eo *o;
|
||||
Eina_Future *future;
|
||||
Efl_Future_Cb_Desc desc;
|
||||
} Efl_Future_Pending;
|
||||
|
||||
|
||||
typedef struct
|
||||
{
|
||||
EINA_INLIST;
|
||||
|
@ -972,48 +983,77 @@ struct _Eo_Callback_Description
|
|||
|
||||
static int _eo_callbacks = 0;
|
||||
static Eina_Mempool *_eo_callback_mempool = NULL;
|
||||
static int _efl_pending_futures = 0;
|
||||
static Eina_Mempool *_efl_pending_future_mempool = NULL;
|
||||
|
||||
static void
|
||||
_eo_callback_free(Eo_Callback_Description *cb)
|
||||
_mempool_data_free(Eina_Mempool **mp, int *usage, void *data)
|
||||
{
|
||||
if (!cb) return;
|
||||
eina_mempool_free(_eo_callback_mempool, cb);
|
||||
_eo_callbacks--;
|
||||
if (_eo_callbacks == 0)
|
||||
if (!data) return;
|
||||
eina_mempool_free(*mp, data);
|
||||
(*usage)--;
|
||||
if (*usage == 0)
|
||||
{
|
||||
eina_mempool_del(_eo_callback_mempool);
|
||||
_eo_callback_mempool = NULL;
|
||||
eina_mempool_del(*mp);
|
||||
*mp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static Eo_Callback_Description *
|
||||
_eo_callback_new(void)
|
||||
static void *
|
||||
_mempool_data_alloc(Eina_Mempool **mp, int *usage, size_t size)
|
||||
{
|
||||
Eo_Callback_Description *cb;
|
||||
// very unlikely that the mempool isnt initted, so take all the init code
|
||||
// and move it out of l1 instruction cache space so we dont pollute the
|
||||
// l1 cache with unused code 99% of the time
|
||||
if (!_eo_callback_mempool) goto init_mempool;
|
||||
if (!*mp) goto init_mempool;
|
||||
init_mempool_back:
|
||||
|
||||
cb = eina_mempool_calloc(_eo_callback_mempool,
|
||||
sizeof(Eo_Callback_Description));
|
||||
cb = eina_mempool_calloc(*mp, size);
|
||||
if (cb)
|
||||
{
|
||||
_eo_callbacks++;
|
||||
(*usage)++;
|
||||
return cb;
|
||||
}
|
||||
if (_eo_callbacks != 0) return NULL;
|
||||
eina_mempool_del(_eo_callback_mempool);
|
||||
_eo_callback_mempool = NULL;
|
||||
if (*usage != 0) return NULL;
|
||||
eina_mempool_del(*mp);
|
||||
*mp = NULL;
|
||||
return NULL;
|
||||
init_mempool:
|
||||
_eo_callback_mempool = eina_mempool_add
|
||||
("chained_mempool", NULL, NULL, sizeof(Eo_Callback_Description), 256);
|
||||
if (!_eo_callback_mempool) return NULL;
|
||||
*mp = eina_mempool_add
|
||||
("chained_mempool", NULL, NULL, size, 256);
|
||||
if (!*mp) return NULL;
|
||||
goto init_mempool_back;
|
||||
}
|
||||
|
||||
static void
|
||||
_eo_callback_free(Eo_Callback_Description *cb)
|
||||
{
|
||||
_mempool_data_free(&_eo_callback_mempool, &_eo_callbacks, cb);
|
||||
}
|
||||
|
||||
static Eo_Callback_Description *
|
||||
_eo_callback_new(void)
|
||||
{
|
||||
return _mempool_data_alloc(&_eo_callback_mempool, &_eo_callbacks,
|
||||
sizeof(Eo_Callback_Description));
|
||||
}
|
||||
|
||||
static void
|
||||
_efl_pending_future_free(Efl_Future_Pending *pending)
|
||||
{
|
||||
_mempool_data_free(&_efl_pending_future_mempool,
|
||||
&_efl_pending_futures, pending);
|
||||
}
|
||||
|
||||
static Efl_Future_Pending *
|
||||
_efl_pending_future_new(void)
|
||||
{
|
||||
return _mempool_data_alloc(&_efl_pending_future_mempool,
|
||||
&_efl_pending_futures,
|
||||
sizeof(Efl_Future_Pending));
|
||||
}
|
||||
|
||||
#ifdef EFL_EVENT_SPECIAL_SKIP
|
||||
|
||||
#define CB_COUNT_INC(cnt) do { if ((cnt) != 0xffff) (cnt)++; } while(0)
|
||||
|
@ -1858,6 +1898,104 @@ EAPI const Eina_Value_Type *EFL_DBG_INFO_TYPE = &_EFL_DBG_INFO_TYPE;
|
|||
/* EFL_OBJECT_CLASS stuff */
|
||||
#define MY_CLASS EFL_OBJECT_CLASS
|
||||
|
||||
static void
|
||||
_efl_pending_futures_clear(Efl_Object_Data *pd)
|
||||
{
|
||||
while (pd->pending_futures)
|
||||
{
|
||||
Efl_Future_Pending *pending = EINA_INLIST_CONTAINER_GET(pd->pending_futures, Efl_Future_Pending);
|
||||
Eina_Future *future = *pending->desc.storage;
|
||||
assert(future);
|
||||
eina_future_cancel(future);
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Value
|
||||
_efl_future_cb(void *data, const Eina_Value value, const Eina_Future *dead_future)
|
||||
{
|
||||
Efl_Future_Pending *pending = data;
|
||||
Eina_Value ret = value;
|
||||
Eo *o;
|
||||
Efl_Object_Data *pd;
|
||||
|
||||
EINA_SAFETY_ON_NULL_GOTO(pending, err);
|
||||
o = pending->o;
|
||||
pd = efl_data_scope_get(o, EFL_OBJECT_CLASS);
|
||||
EINA_SAFETY_ON_NULL_GOTO(pd, err);
|
||||
|
||||
pd->pending_futures = eina_inlist_remove(pd->pending_futures,
|
||||
EINA_INLIST_GET(pending));
|
||||
efl_ref(o);
|
||||
EASY_FUTURE_DISPATCH(ret, value, dead_future, &pending->desc, o);
|
||||
efl_unref(o);
|
||||
_efl_pending_future_free(pending);
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
eina_value_setup(&ret, EINA_VALUE_TYPE_ERROR);
|
||||
eina_value_set(&ret, ENOMEM);
|
||||
return ret;
|
||||
}
|
||||
|
||||
EOAPI Eina_Future_Desc
|
||||
efl_future_cb_from_desc(Eo *o, const Efl_Future_Cb_Desc desc)
|
||||
{
|
||||
Efl_Future_Pending *pending = NULL;
|
||||
Eina_Future **storage = NULL;
|
||||
Efl_Object_Data *pd;
|
||||
|
||||
EINA_SAFETY_ON_NULL_GOTO(o, end);
|
||||
pd = efl_data_scope_get(o, EFL_OBJECT_CLASS);
|
||||
EINA_SAFETY_ON_NULL_GOTO(pd, end);
|
||||
pending = _efl_pending_future_new();
|
||||
EINA_SAFETY_ON_NULL_GOTO(pending, end);
|
||||
memcpy(&pending->desc, &desc, sizeof(Efl_Future_Cb_Desc));
|
||||
pending->o = o;
|
||||
pending->future = NULL;
|
||||
if (!pending->desc.storage) pending->desc.storage = &pending->future;
|
||||
pd->pending_futures = eina_inlist_append(pd->pending_futures,
|
||||
EINA_INLIST_GET(pending));
|
||||
storage = pending->desc.storage;
|
||||
end:
|
||||
return (Eina_Future_Desc){ .cb = _efl_future_cb, .data = pending, .storage = storage };
|
||||
}
|
||||
|
||||
EOAPI Eina_Future *
|
||||
efl_future_chain_from_array(Eo *obj,
|
||||
Eina_Future *prev,
|
||||
const Efl_Future_Cb_Desc descs[])
|
||||
{
|
||||
size_t i = -1;
|
||||
Eina_Future *f = prev;
|
||||
|
||||
for (i = 0; descs[i].success || descs[i].error || descs[i].free || descs[i].success_type; i++)
|
||||
{
|
||||
Eina_Future_Desc eina_desc = efl_future_cb_from_desc(obj, descs[i]);
|
||||
f = eina_future_then_from_desc(f, eina_desc);
|
||||
EINA_SAFETY_ON_NULL_GOTO(f, err);
|
||||
}
|
||||
|
||||
return f;
|
||||
|
||||
err:
|
||||
/*
|
||||
There's no need to cancel the futures, since eina_future_then_from_desc()
|
||||
will cancel the whole chain in case of failure.
|
||||
All we need to do is to free the remaining descs
|
||||
*/
|
||||
for (i = i + 1; descs[i].error || descs[i].free; i++)
|
||||
{
|
||||
if (descs[i].error)
|
||||
{
|
||||
Eina_Value r = descs[i].error(obj, ENOMEM);
|
||||
if (r.type) eina_value_flush(&r);
|
||||
}
|
||||
if (descs[i].free) descs[i].free(obj, NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EOLIAN static Eo *
|
||||
_efl_object_constructor(Eo *obj, Efl_Object_Data *pd EINA_UNUSED)
|
||||
{
|
||||
|
@ -1905,6 +2043,7 @@ composite_obj_back:
|
|||
if (pd->parent) goto err_parent;
|
||||
err_parent_back:
|
||||
|
||||
_efl_pending_futures_clear(pd);
|
||||
_eo_generic_data_del_all(obj, pd);
|
||||
_wref_destruct(pd);
|
||||
_eo_callback_remove_all(pd);
|
||||
|
|
Loading…
Reference in New Issue